001package io.ebeaninternal.dbmigration.model; 002 003import io.ebean.config.DbConstraintNaming; 004import io.ebean.config.dbplatform.DatabasePlatform; 005import io.ebeaninternal.api.SpiEbeanServer; 006import io.ebeaninternal.dbmigration.Detect; 007import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; 008import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; 009import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; 010import io.ebeaninternal.dbmigration.migration.ChangeSet; 011import io.ebeaninternal.dbmigration.model.build.ModelBuildBeanVisitor; 012import io.ebeaninternal.dbmigration.model.build.ModelBuildContext; 013import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing; 014import io.ebeaninternal.extraddl.model.DdlScript; 015import io.ebeaninternal.extraddl.model.ExtraDdl; 016import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader; 017import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder; 018 019import java.io.IOException; 020import java.util.List; 021 022import static io.ebeaninternal.api.PlatformMatch.matchPlatform; 023 024/** 025 * Reads EbeanServer bean descriptors to build the current model. 026 */ 027public class CurrentModel { 028 029 private final SpiEbeanServer server; 030 031 private final DatabasePlatform databasePlatform; 032 033 private final DbConstraintNaming constraintNaming; 034 035 private final boolean platformTypes; 036 037 private final boolean jaxbPresent; 038 039 private final String ddlHeader; 040 041 private ModelContainer model; 042 043 private ChangeSet changeSet; 044 045 private DdlWrite write; 046 047 private DdlOptions ddlOptions = new DdlOptions(); 048 049 /** 050 * Construct with a given EbeanServer instance for DDL create all generation, not migration. 051 */ 052 public CurrentModel(SpiEbeanServer server) { 053 this(server, server.getServerConfig().getConstraintNaming(), true); 054 } 055 056 /** 057 * Construct with a given EbeanServer, platformDdl and constraintNaming convention. 058 * <p> 059 * Note the EbeanServer is just used to read the BeanDescriptors and platformDdl supplies 060 * the platform specific handling on 061 * </p> 062 */ 063 public CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming) { 064 this(server, constraintNaming, false); 065 } 066 067 private CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming, boolean platformTypes) { 068 this.server = server; 069 this.databasePlatform = server.getDatabasePlatform(); 070 this.constraintNaming = constraintNaming; 071 this.platformTypes = platformTypes; 072 this.ddlHeader = server.getServerConfig().getDdlHeader(); 073 this.jaxbPresent = Detect.isJAXBPresent(server.getServerConfig()); 074 } 075 076 public DdlOptions getDdlOptions() { 077 return ddlOptions; 078 } 079 080 /** 081 * Return true if the model contains tables that are partitioned. 082 */ 083 public boolean isTablePartitioning() { 084 return read().isTablePartitioning(); 085 } 086 087 /** 088 * Return the tables that have partitioning. 089 */ 090 public List<MTable> getPartitionedTables() { 091 return read().getPartitionedTables(); 092 } 093 094 /** 095 * Return the current model by reading all the bean descriptors and properties. 096 */ 097 public ModelContainer read() { 098 if (model == null) { 099 model = new ModelContainer(); 100 ModelBuildContext context = new ModelBuildContext(model, databasePlatform, constraintNaming, platformTypes); 101 ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); 102 VisitAllUsing visit = new VisitAllUsing(visitor, server); 103 visit.visitAllBeans(); 104 // adjust the foreign keys on the 'draft' tables 105 context.adjustDraftReferences(); 106 } 107 return model; 108 } 109 110 public void setChangeSet(ChangeSet changeSet) { 111 this.changeSet = changeSet; 112 } 113 114 /** 115 * Return as a ChangeSet. 116 */ 117 public ChangeSet getChangeSet() { 118 read(); 119 if (changeSet == null) { 120 changeSet = asChangeSet(); 121 } 122 return changeSet; 123 } 124 125 /** 126 * Return the 'Create' DDL. 127 */ 128 public String getCreateDdl() throws IOException { 129 130 createDdl(); 131 132 StringBuilder ddl = new StringBuilder(2000); 133 if (ddlHeader != null && !ddlHeader.isEmpty()) { 134 ddl.append(ddlHeader).append('\n'); 135 } 136 if (jaxbPresent) { 137 addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script "); 138 } 139 ddl.append(write.apply().getBuffer()); 140 ddl.append(write.applyForeignKeys().getBuffer()); 141 ddl.append(write.applyHistoryView().getBuffer()); 142 ddl.append(write.applyHistoryTrigger().getBuffer()); 143 return ddl.toString(); 144 } 145 146 private void addExtraDdl(StringBuilder ddl, ExtraDdl extraDdl, String prefix) { 147 if (extraDdl != null) { 148 List<DdlScript> ddlScript = extraDdl.getDdlScript(); 149 for (DdlScript script : ddlScript) { 150 if (script.isInit() && matchPlatform(server.getDatabasePlatform().getPlatform(), script.getPlatforms())) { 151 ddl.append(prefix).append(script.getName()).append('\n'); 152 ddl.append(script.getValue()); 153 } 154 } 155 } 156 } 157 158 /** 159 * Return the 'Drop' DDL. 160 */ 161 public String getDropAllDdl() throws IOException { 162 163 createDdl(); 164 165 StringBuilder ddl = new StringBuilder(2000); 166 if (ddlHeader != null && !ddlHeader.isEmpty()) { 167 ddl.append(ddlHeader).append('\n'); 168 } 169 ddl.append(write.dropAllForeignKeys().getBuffer()); 170 ddl.append(write.dropAll().getBuffer()); 171 return ddl.toString(); 172 } 173 174 /** 175 * Create all the DDL based on the changeSet. 176 */ 177 private void createDdl() throws IOException { 178 if (write == null) { 179 ChangeSet createChangeSet = getChangeSet(); 180 write = new DdlWrite(new MConfiguration(), model, ddlOptions); 181 DdlHandler handler = handler(); 182 handler.generateProlog(write); 183 handler.generate(write, createChangeSet); 184 handler.generateEpilog(write); 185 } 186 } 187 188 /** 189 * Return the platform specific DdlHandler (to generate DDL). 190 */ 191 private DdlHandler handler() { 192 return PlatformDdlBuilder.create(databasePlatform).createDdlHandler(server.getServerConfig()); 193 } 194 195 /** 196 * Convert the model into a ChangeSet. 197 */ 198 private ChangeSet asChangeSet() { 199 // empty diff so changes will effectively all be create 200 ModelDiff diff = new ModelDiff(); 201 diff.compareTo(model); 202 return diff.getApplyChangeSet(); 203 } 204 205}