001package io.ebeaninternal.dbmigration.model; 002 003import io.ebeaninternal.dbmigration.migration.AddColumn; 004import io.ebeaninternal.dbmigration.migration.AddHistoryTable; 005import io.ebeaninternal.dbmigration.migration.AddTableComment; 006import io.ebeaninternal.dbmigration.migration.AddUniqueConstraint; 007import io.ebeaninternal.dbmigration.migration.AlterColumn; 008import io.ebeaninternal.dbmigration.migration.AlterForeignKey; 009import io.ebeaninternal.dbmigration.migration.ChangeSet; 010import io.ebeaninternal.dbmigration.migration.ChangeSetType; 011import io.ebeaninternal.dbmigration.migration.CreateIndex; 012import io.ebeaninternal.dbmigration.migration.DropColumn; 013import io.ebeaninternal.dbmigration.migration.DropHistoryTable; 014import io.ebeaninternal.dbmigration.migration.DropIndex; 015import io.ebeaninternal.dbmigration.migration.Migration; 016 017import java.util.ArrayList; 018import java.util.List; 019import java.util.Map; 020 021/** 022 * Used to prepare a diff in terms of changes required to migrate from 023 * the base model to the newer model. 024 */ 025public class ModelDiff { 026 027 /** 028 * The base model to which we compare the newer model. 029 */ 030 private final ModelContainer baseModel; 031 032 /** 033 * List of 'create' type changes. 034 */ 035 private final List<Object> applyChanges = new ArrayList<>(); 036 037 /** 038 * List of 'drop' type changes. Expected to be placed into a separate DDL script. 039 */ 040 private final List<Object> dropChanges = new ArrayList<>(); 041 042 /** 043 * Construct with a base model. 044 */ 045 public ModelDiff(ModelContainer baseModel) { 046 this.baseModel = baseModel; 047 } 048 049 /** 050 * Construct with a base model. 051 */ 052 public ModelDiff() { 053 this.baseModel = new ModelContainer(); 054 } 055 056 /** 057 * Return true if apply and drop changes are both empty. This means there are no migration changes. 058 */ 059 public boolean isEmpty() { 060 return applyChanges.isEmpty() && dropChanges.isEmpty(); 061 } 062 063 /** 064 * Return the diff as a migration potentially containing an apply changeSet and a drop changeSet. 065 */ 066 public Migration getMigration() { 067 Migration migration = new Migration(); 068 if (!applyChanges.isEmpty()) { 069 // add a non-empty apply changeSet 070 migration.getChangeSet().add(getApplyChangeSet()); 071 } 072 if (!dropChanges.isEmpty()) { 073 // add a non-empty drop changeSet 074 migration.getChangeSet().add(getDropChangeSet()); 075 } 076 return migration; 077 } 078 079 /** 080 * Return the list of 'apply' changes. 081 */ 082 List<Object> getApplyChanges() { 083 return applyChanges; 084 } 085 086 /** 087 * Return the list of 'drop' changes. 088 */ 089 List<Object> getDropChanges() { 090 return dropChanges; 091 } 092 093 /** 094 * Return the 'apply' changeSet. 095 */ 096 public ChangeSet getApplyChangeSet() { 097 // put the changes into a ChangeSet 098 ChangeSet applyChangeSet = new ChangeSet(); 099 applyChangeSet.setType(ChangeSetType.APPLY); 100 applyChangeSet.getChangeSetChildren().addAll(applyChanges); 101 return applyChangeSet; 102 } 103 104 /** 105 * Return the 'drop' changeSet. 106 */ 107 ChangeSet getDropChangeSet() { 108 // put the changes into a ChangeSet 109 ChangeSet createChangeSet = new ChangeSet(); 110 createChangeSet.setType(ChangeSetType.PENDING_DROPS); 111 createChangeSet.getChangeSetChildren().addAll(dropChanges); 112 return createChangeSet; 113 } 114 115 /** 116 * Compare to a 'newer' model and collect the differences. 117 */ 118 public void compareTo(ModelContainer newModel) { 119 Map<String, MTable> newTables = newModel.getTables(); 120 for (MTable newTable : newTables.values()) { 121 122 MTable currentTable = baseModel.getTable(newTable.getName()); 123 if (currentTable == null) { 124 addNewTable(newTable); 125 } else { 126 compareTables(currentTable, newTable); 127 } 128 } 129 130 // search for tables that are no longer used 131 for (MTable existingTable : baseModel.getTables().values()) { 132 if (!newTables.containsKey(existingTable.getName())) { 133 addDropTable(existingTable); 134 } 135 } 136 137 for (MIndex newIndex : newModel.allIndexes()) { 138 MIndex currentIndex = baseModel.getIndex(newIndex); 139 if (currentIndex == null) { 140 addCreateIndex(newIndex.createIndex()); 141 } else { 142 compareIndexes(currentIndex, newIndex); 143 } 144 } 145 146 // search for indexes that are no longer used 147 for (MIndex existingIndex : baseModel.allIndexes()) { 148 if (newModel.dropIndex(existingIndex)) { 149 addDropIndex(existingIndex.dropIndex()); 150 } 151 } 152 153 // register un-applied ones from the previous migrations 154 baseModel.registerPendingHistoryDropColumns(newModel); 155 if (!dropChanges.isEmpty()) { 156 // register new ones created just now as part of this diff 157 newModel.registerPendingHistoryDropColumns(getDropChangeSet()); 158 } 159 } 160 161 protected void addDropTable(MTable existingTable) { 162 dropChanges.add(existingTable.dropTable()); 163 } 164 165 /** 166 * Add CreateTable to the 'apply' changes. 167 */ 168 protected void addNewTable(MTable newTable) { 169 applyChanges.add(newTable.createTable()); 170 } 171 172 /** 173 * Compare tables looking for add/drop/modify columns etc. 174 */ 175 protected void compareTables(MTable currentTable, MTable newTable) { 176 currentTable.compare(this, newTable); 177 } 178 179 /** 180 * Compare tables looking for add/drop/modify columns etc. 181 */ 182 protected void compareIndexes(MIndex currentIndex, MIndex newIndex) { 183 currentIndex.compare(this, newIndex); 184 } 185 186 /** 187 * Add the AlterColumn to the 'apply' changes. 188 */ 189 public void addAlterColumn(AlterColumn alterColumn) { 190 applyChanges.add(alterColumn); 191 } 192 193 /** 194 * Add the AlterColumn to the 'apply' changes. 195 */ 196 public void addAddColumn(AddColumn addColumn) { 197 applyChanges.add(addColumn); 198 } 199 200 /** 201 * Add the DropColumn to the 'drop' changes. 202 */ 203 public void addDropColumn(DropColumn dropColumn) { 204 dropChanges.add(dropColumn); 205 } 206 207 /** 208 * Add the AddHistoryTable to apply changes. 209 */ 210 public void addAddHistoryTable(AddHistoryTable addHistoryTable) { 211 applyChanges.add(addHistoryTable); 212 } 213 214 /** 215 * Add the DropHistoryTable to the 'drop history' changes. 216 */ 217 public void addDropHistoryTable(DropHistoryTable dropHistoryTable) { 218 dropChanges.add(dropHistoryTable); 219 } 220 221 /** 222 * Add the DropIndex to the 'apply' changes. 223 */ 224 public void addDropIndex(DropIndex dropIndex) { 225 applyChanges.add(dropIndex); 226 } 227 228 /** 229 * Add the CreateIndex to the 'apply' changes. 230 */ 231 public void addCreateIndex(CreateIndex createIndex) { 232 applyChanges.add(createIndex); 233 } 234 235 /** 236 * Add a table comment to the 'apply' changes. 237 */ 238 public void addTableComment(AddTableComment addTableComment) { 239 applyChanges.add(addTableComment); 240 } 241 242 /** 243 * Adds (or drops) a unique constraint to the 'apply' changes. 244 */ 245 public void addUniqueConstraint(AddUniqueConstraint addUniqueConstraint) { 246 applyChanges.add(addUniqueConstraint); 247 } 248 249 /** 250 * Adds (or drops) a foreign key constraint to the 'apply' changes. 251 */ 252 public void addAlterForeignKey(AlterForeignKey alterForeignKey) { 253 applyChanges.add(alterForeignKey); 254 } 255}