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