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 writer; 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() { 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 132 try { 133 writer.writeApply(ddl); 134 } catch (IOException e) { // should not happen on StringBuilder 135 throw new RuntimeException(e); 136 } 137 138 return ddl.toString(); 139 } 140 141 private void addExtraDdl(StringBuilder ddl, ExtraDdl extraDdl, String prefix) { 142 if (extraDdl != null) { 143 List<DdlScript> ddlScript = extraDdl.getDdlScript(); 144 for (DdlScript script : ddlScript) { 145 if (script.isInit() && matchPlatform(server.platform(), script.getPlatforms())) { 146 ddl.append(prefix).append(script.getName()).append('\n'); 147 ddl.append(script.getValue()); 148 } 149 } 150 } 151 } 152 153 /** 154 * Return the 'Drop' DDL. 155 */ 156 public String getDropAllDdl() { 157 158 createDdl(); 159 160 StringBuilder ddl = new StringBuilder(2000); 161 if (ddlHeader != null && !ddlHeader.isEmpty()) { 162 ddl.append(ddlHeader).append('\n'); 163 } 164 try { 165 writer.writeDropAll(ddl); 166 } catch (IOException e) { // should not happen on StringBuilder 167 throw new RuntimeException(e); 168 } 169 return ddl.toString(); 170 } 171 172 /** 173 * Create all the DDL based on the changeSet. 174 */ 175 private void createDdl() { 176 if (writer == null) { 177 ChangeSet createChangeSet = getChangeSet(); 178 writer = new DdlWrite(new MConfiguration(), model, ddlOptions); 179 DdlHandler handler = handler(); 180 handler.generateProlog(writer); 181 handler.generate(writer, createChangeSet); 182 handler.generateEpilog(writer); 183 } 184 } 185 186 /** 187 * Return the platform specific DdlHandler (to generate DDL). 188 */ 189 private DdlHandler handler() { 190 return PlatformDdlBuilder.create(databasePlatform).createDdlHandler(server.config()); 191 } 192 193 /** 194 * Convert the model into a ChangeSet. 195 */ 196 private ChangeSet asChangeSet() { 197 // empty diff so changes will effectively all be create 198 ModelDiff diff = new ModelDiff(); 199 diff.compareTo(model); 200 return diff.getApplyChangeSet(); 201 } 202 203}