001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jdbc.datasource.embedded; 018 019import javax.sql.DataSource; 020 021import org.springframework.core.io.DefaultResourceLoader; 022import org.springframework.core.io.ResourceLoader; 023import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; 024import org.springframework.jdbc.datasource.init.ScriptUtils; 025import org.springframework.util.Assert; 026 027/** 028 * A builder that provides a convenient API for constructing an embedded database. 029 * 030 * <h3>Usage Example</h3> 031 * <pre class="code"> 032 * EmbeddedDatabase db = new EmbeddedDatabaseBuilder() 033 * .generateUniqueName(true) 034 * .setType(H2) 035 * .setScriptEncoding("UTF-8") 036 * .ignoreFailedDrops(true) 037 * .addScript("schema.sql") 038 * .addScripts("user_data.sql", "country_data.sql") 039 * .build(); 040 * 041 * // perform actions against the db (EmbeddedDatabase extends javax.sql.DataSource) 042 * 043 * db.shutdown(); 044 * </pre> 045 * 046 * @author Keith Donald 047 * @author Juergen Hoeller 048 * @author Dave Syer 049 * @author Sam Brannen 050 * @since 3.0 051 * @see org.springframework.jdbc.datasource.init.ScriptUtils 052 * @see org.springframework.jdbc.datasource.init.ResourceDatabasePopulator 053 * @see org.springframework.jdbc.datasource.init.DatabasePopulatorUtils 054 */ 055public class EmbeddedDatabaseBuilder { 056 057 private final EmbeddedDatabaseFactory databaseFactory; 058 059 private final ResourceDatabasePopulator databasePopulator; 060 061 private final ResourceLoader resourceLoader; 062 063 064 /** 065 * Create a new embedded database builder with a {@link DefaultResourceLoader}. 066 */ 067 public EmbeddedDatabaseBuilder() { 068 this(new DefaultResourceLoader()); 069 } 070 071 /** 072 * Create a new embedded database builder with the given {@link ResourceLoader}. 073 * @param resourceLoader the {@code ResourceLoader} to delegate to 074 */ 075 public EmbeddedDatabaseBuilder(ResourceLoader resourceLoader) { 076 this.databaseFactory = new EmbeddedDatabaseFactory(); 077 this.databasePopulator = new ResourceDatabasePopulator(); 078 this.databaseFactory.setDatabasePopulator(this.databasePopulator); 079 this.resourceLoader = resourceLoader; 080 } 081 082 /** 083 * Specify whether a unique ID should be generated and used as the database name. 084 * <p>If the configuration for this builder is reused across multiple 085 * application contexts within a single JVM, this flag should be <em>enabled</em> 086 * (i.e., set to {@code true}) in order to ensure that each application context 087 * gets its own embedded database. 088 * <p>Enabling this flag overrides any explicit name set via {@link #setName}. 089 * @param flag {@code true} if a unique database name should be generated 090 * @return {@code this}, to facilitate method chaining 091 * @since 4.2 092 * @see #setName 093 */ 094 public EmbeddedDatabaseBuilder generateUniqueName(boolean flag) { 095 this.databaseFactory.setGenerateUniqueDatabaseName(flag); 096 return this; 097 } 098 099 /** 100 * Set the name of the embedded database. 101 * <p>Defaults to {@link EmbeddedDatabaseFactory#DEFAULT_DATABASE_NAME} if 102 * not called. 103 * <p>Will be overridden if the {@code generateUniqueName} flag has been 104 * set to {@code true}. 105 * @param databaseName the name of the embedded database to build 106 * @return {@code this}, to facilitate method chaining 107 * @see #generateUniqueName 108 */ 109 public EmbeddedDatabaseBuilder setName(String databaseName) { 110 this.databaseFactory.setDatabaseName(databaseName); 111 return this; 112 } 113 114 /** 115 * Set the type of embedded database. 116 * <p>Defaults to HSQL if not called. 117 * @param databaseType the type of embedded database to build 118 * @return {@code this}, to facilitate method chaining 119 */ 120 public EmbeddedDatabaseBuilder setType(EmbeddedDatabaseType databaseType) { 121 this.databaseFactory.setDatabaseType(databaseType); 122 return this; 123 } 124 125 /** 126 * Set the factory to use to create the {@link DataSource} instance that 127 * connects to the embedded database. 128 * <p>Defaults to {@link SimpleDriverDataSourceFactory} but can be overridden, 129 * for example to introduce connection pooling. 130 * @return {@code this}, to facilitate method chaining 131 * @since 4.0.3 132 */ 133 public EmbeddedDatabaseBuilder setDataSourceFactory(DataSourceFactory dataSourceFactory) { 134 Assert.notNull(dataSourceFactory, "DataSourceFactory is required"); 135 this.databaseFactory.setDataSourceFactory(dataSourceFactory); 136 return this; 137 } 138 139 /** 140 * Add default SQL scripts to execute to populate the database. 141 * <p>The default scripts are {@code "schema.sql"} to create the database 142 * schema and {@code "data.sql"} to populate the database with data. 143 * @return {@code this}, to facilitate method chaining 144 */ 145 public EmbeddedDatabaseBuilder addDefaultScripts() { 146 return addScripts("schema.sql", "data.sql"); 147 } 148 149 /** 150 * Add an SQL script to execute to initialize or populate the database. 151 * @param script the script to execute 152 * @return {@code this}, to facilitate method chaining 153 */ 154 public EmbeddedDatabaseBuilder addScript(String script) { 155 this.databasePopulator.addScript(this.resourceLoader.getResource(script)); 156 return this; 157 } 158 159 /** 160 * Add multiple SQL scripts to execute to initialize or populate the database. 161 * @param scripts the scripts to execute 162 * @return {@code this}, to facilitate method chaining 163 * @since 4.0.3 164 */ 165 public EmbeddedDatabaseBuilder addScripts(String... scripts) { 166 for (String script : scripts) { 167 addScript(script); 168 } 169 return this; 170 } 171 172 /** 173 * Specify the character encoding used in all SQL scripts, if different from 174 * the platform encoding. 175 * @param scriptEncoding the encoding used in scripts 176 * @return {@code this}, to facilitate method chaining 177 * @since 4.0.3 178 */ 179 public EmbeddedDatabaseBuilder setScriptEncoding(String scriptEncoding) { 180 this.databasePopulator.setSqlScriptEncoding(scriptEncoding); 181 return this; 182 } 183 184 /** 185 * Specify the statement separator used in all SQL scripts, if a custom one. 186 * <p>Defaults to {@code ";"} if not specified and falls back to {@code "\n"} 187 * as a last resort; may be set to {@link ScriptUtils#EOF_STATEMENT_SEPARATOR} 188 * to signal that each script contains a single statement without a separator. 189 * @param separator the statement separator 190 * @return {@code this}, to facilitate method chaining 191 * @since 4.0.3 192 */ 193 public EmbeddedDatabaseBuilder setSeparator(String separator) { 194 this.databasePopulator.setSeparator(separator); 195 return this; 196 } 197 198 /** 199 * Specify the single-line comment prefix used in all SQL scripts. 200 * <p>Defaults to {@code "--"}. 201 * @param commentPrefix the prefix for single-line comments 202 * @return {@code this}, to facilitate method chaining 203 * @since 4.0.3 204 * @see #setCommentPrefixes(String...) 205 */ 206 public EmbeddedDatabaseBuilder setCommentPrefix(String commentPrefix) { 207 this.databasePopulator.setCommentPrefix(commentPrefix); 208 return this; 209 } 210 211 /** 212 * Specify the prefixes that identify single-line comments within all SQL scripts. 213 * <p>Defaults to {@code ["--"]}. 214 * @param commentPrefixes the prefixes for single-line comments 215 * @return {@code this}, to facilitate method chaining 216 * @since 5.2 217 */ 218 public EmbeddedDatabaseBuilder setCommentPrefixes(String... commentPrefixes) { 219 this.databasePopulator.setCommentPrefixes(commentPrefixes); 220 return this; 221 } 222 223 /** 224 * Specify the start delimiter for block comments in all SQL scripts. 225 * <p>Defaults to {@code "/*"}. 226 * @param blockCommentStartDelimiter the start delimiter for block comments 227 * @return {@code this}, to facilitate method chaining 228 * @since 4.0.3 229 * @see #setBlockCommentEndDelimiter 230 */ 231 public EmbeddedDatabaseBuilder setBlockCommentStartDelimiter(String blockCommentStartDelimiter) { 232 this.databasePopulator.setBlockCommentStartDelimiter(blockCommentStartDelimiter); 233 return this; 234 } 235 236 /** 237 * Specify the end delimiter for block comments in all SQL scripts. 238 * <p>Defaults to <code>"*/"</code>. 239 * @param blockCommentEndDelimiter the end delimiter for block comments 240 * @return {@code this}, to facilitate method chaining 241 * @since 4.0.3 242 * @see #setBlockCommentStartDelimiter 243 */ 244 public EmbeddedDatabaseBuilder setBlockCommentEndDelimiter(String blockCommentEndDelimiter) { 245 this.databasePopulator.setBlockCommentEndDelimiter(blockCommentEndDelimiter); 246 return this; 247 } 248 249 /** 250 * Specify that all failures which occur while executing SQL scripts should 251 * be logged but should not cause a failure. 252 * <p>Defaults to {@code false}. 253 * @param flag {@code true} if script execution should continue on error 254 * @return {@code this}, to facilitate method chaining 255 * @since 4.0.3 256 */ 257 public EmbeddedDatabaseBuilder continueOnError(boolean flag) { 258 this.databasePopulator.setContinueOnError(flag); 259 return this; 260 } 261 262 /** 263 * Specify that a failed SQL {@code DROP} statement within an executed 264 * script can be ignored. 265 * <p>This is useful for a database whose SQL dialect does not support an 266 * {@code IF EXISTS} clause in a {@code DROP} statement. 267 * <p>The default is {@code false} so that {@link #build building} will fail 268 * fast if a script starts with a {@code DROP} statement. 269 * @param flag {@code true} if failed drop statements should be ignored 270 * @return {@code this}, to facilitate method chaining 271 * @since 4.0.3 272 */ 273 public EmbeddedDatabaseBuilder ignoreFailedDrops(boolean flag) { 274 this.databasePopulator.setIgnoreFailedDrops(flag); 275 return this; 276 } 277 278 /** 279 * Build the embedded database. 280 * @return the embedded database 281 */ 282 public EmbeddedDatabase build() { 283 return this.databaseFactory.getDatabase(); 284 } 285 286}