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 /** 058 * Return true if the apply and drop changes are both empty. 059 * This means there are no migration changes. 060 */ 061 public boolean isEmpty() { 062 return applyChanges.isEmpty() && dropChanges.isEmpty(); 063 } 064 065 /** 066 * Return the diff as a migration potentially containing 067 * an apply changeSet and a drop changeSet. 068 */ 069 public Migration getMigration() { 070 071 Migration migration = new Migration(); 072 if (!applyChanges.isEmpty()) { 073 // add a non empty apply changeSet 074 migration.getChangeSet().add(getApplyChangeSet()); 075 } 076 077 if (!dropChanges.isEmpty()) { 078 // add a non empty drop changeSet 079 migration.getChangeSet().add(getDropChangeSet()); 080 } 081 return migration; 082 } 083 084 /** 085 * Return the list of 'apply' changes. 086 */ 087 List<Object> getApplyChanges() { 088 return applyChanges; 089 } 090 091 /** 092 * Return the list of 'drop' changes. 093 */ 094 List<Object> getDropChanges() { 095 return dropChanges; 096 } 097 098 /** 099 * Return the 'apply' changeSet. 100 */ 101 public ChangeSet getApplyChangeSet() { 102 // put the changes into a ChangeSet 103 ChangeSet applyChangeSet = new ChangeSet(); 104 applyChangeSet.setType(ChangeSetType.APPLY); 105 applyChangeSet.getChangeSetChildren().addAll(applyChanges); 106 return applyChangeSet; 107 } 108 109 /** 110 * Return the 'drop' changeSet. 111 */ 112 ChangeSet getDropChangeSet() { 113 // put the changes into a ChangeSet 114 ChangeSet createChangeSet = new ChangeSet(); 115 createChangeSet.setType(ChangeSetType.PENDING_DROPS); 116 createChangeSet.getChangeSetChildren().addAll(dropChanges); 117 return createChangeSet; 118 } 119 120 /** 121 * Compare to a 'newer' model and collect the differences. 122 */ 123 public void compareTo(ModelContainer newModel) { 124 125 Map<String, MTable> newTables = newModel.getTables(); 126 for (MTable newTable : newTables.values()) { 127 128 MTable currentTable = baseModel.getTable(newTable.getName()); 129 if (currentTable == null) { 130 addNewTable(newTable); 131 } else { 132 compareTables(currentTable, newTable); 133 } 134 } 135 136 // search for tables that are no longer used 137 for (MTable existingTable : baseModel.getTables().values()) { 138 if (!newTables.containsKey(existingTable.getName())) { 139 addDropTable(existingTable); 140 } 141 } 142 143 for (MIndex newIndex : newModel.allIndexes()) { 144 MIndex currentIndex = baseModel.getIndex(newIndex); 145 if (currentIndex == null) { 146 addCreateIndex(newIndex.createIndex()); 147 } else { 148 compareIndexes(currentIndex, newIndex); 149 } 150 } 151 152 // search for indexes that are no longer used 153 for (MIndex existingIndex : baseModel.allIndexes()) { 154 if (newModel.dropIndex(existingIndex)) { 155 addDropIndex(existingIndex.dropIndex()); 156 } 157 } 158 159 // register un-applied ones from the previous migrations 160 baseModel.registerPendingHistoryDropColumns(newModel); 161 if (!dropChanges.isEmpty()) { 162 // register new ones created just now as part of this diff 163 newModel.registerPendingHistoryDropColumns(getDropChangeSet()); 164 } 165 } 166 167 protected void addDropTable(MTable existingTable) { 168 dropChanges.add(existingTable.dropTable()); 169 } 170 171 /** 172 * Add CreateTable to the 'apply' changes. 173 */ 174 protected void addNewTable(MTable newTable) { 175 applyChanges.add(newTable.createTable()); 176 } 177 178 /** 179 * Compare tables looking for add/drop/modify columns etc. 180 */ 181 protected void compareTables(MTable currentTable, MTable newTable) { 182 183 currentTable.compare(this, newTable); 184 } 185 186 /** 187 * Compare tables looking for add/drop/modify columns etc. 188 */ 189 protected void compareIndexes(MIndex currentIndex, MIndex newIndex) { 190 191 currentIndex.compare(this, newIndex); 192 } 193 194 /** 195 * Add the AlterColumn to the 'apply' changes. 196 */ 197 public void addAlterColumn(AlterColumn alterColumn) { 198 applyChanges.add(alterColumn); 199 } 200 201 /** 202 * Add the AlterColumn to the 'apply' changes. 203 */ 204 public void addAddColumn(AddColumn addColumn) { 205 applyChanges.add(addColumn); 206 } 207 208 /** 209 * Add the DropColumn to the 'drop' changes. 210 */ 211 public void addDropColumn(DropColumn dropColumn) { 212 dropChanges.add(dropColumn); 213 } 214 215 /** 216 * Add the AddHistoryTable to apply changes. 217 */ 218 public void addAddHistoryTable(AddHistoryTable addHistoryTable) { 219 applyChanges.add(addHistoryTable); 220 } 221 222 /** 223 * Add the DropHistoryTable to the 'drop history' changes. 224 */ 225 public void addDropHistoryTable(DropHistoryTable dropHistoryTable) { 226 dropChanges.add(dropHistoryTable); 227 } 228 229 /** 230 * Add the DropIndex to the 'apply' changes. 231 */ 232 public void addDropIndex(DropIndex dropIndex) { 233 applyChanges.add(dropIndex); 234 } 235 236 /** 237 * Add the CreateIndex to the 'apply' changes. 238 */ 239 public void addCreateIndex(CreateIndex createIndex) { 240 applyChanges.add(createIndex); 241 } 242 243 /** 244 * Add a table comment to the 'apply' changes. 245 */ 246 public void addTableComment(AddTableComment addTableComment) { 247 applyChanges.add(addTableComment); 248 } 249 250 /** 251 * Adds (or drops) a unique constraint to the 'apply' changes. 252 */ 253 public void addUniqueConstraint(AddUniqueConstraint addUniqueConstraint) { 254 applyChanges.add(addUniqueConstraint); 255 } 256 257 /** 258 * Adds (or drops) a foreign key constraint to the 'apply' changes. 259 */ 260 public void addAlterForeignKey(AlterForeignKey alterForeignKey) { 261 applyChanges.add(alterForeignKey); 262 } 263}