Introduce @SqlConfig for common SQL script config
Prior to this commit, @Sql provided attributes for configuring the syntax of the referenced SQL scripts as well as exception handling and transaction behavior; however, such configuration could not be reused across @Sql declarations thus requiring developers to copy-and-paste common configuration and resulting in unnecessary code duplication. This commit addresses this issue by introducing a new @SqlConfig annotation that can be used to declare common, global configuration for SQL scripts that can be reused within a test class hierarchy. - Introduced top-level @SqlConfig annotation and extracted common configuration attributes from @Sql. - @SqlConfig can be used at the class level for common, global config or via the new 'config' attribute of @Sql for local config. - Introduced MergedSqlConfig as a holder for the merged values from local and global @SqlConfig instances. MergedSqlConfig also contains the logic for overriding global configuration with local configuration. - Refactored all attributes of @SqlConfig to be either of type String or custom enums in order to support overriding. Empty Strings or DEFAULT enum values imply the use of a default or inherited value. Issue: SPR-11896
This commit is contained in:
parent
4e25a14f6c
commit
628806e85b
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2014 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.test.context.jdbc;
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.core.style.ToStringCreator;
|
||||||
|
import org.springframework.jdbc.datasource.init.ScriptUtils;
|
||||||
|
import org.springframework.test.context.jdbc.SqlConfig.ErrorMode;
|
||||||
|
import org.springframework.test.context.jdbc.SqlConfig.TransactionMode;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code MergedSqlConfig} encapsulates the <em>merged</em> SQL script configuration
|
||||||
|
* declared via {@link SqlConfig @SqlConfig} on a test method and its enclosing
|
||||||
|
* test class.
|
||||||
|
* <p>Explicit method-level configuration overrides class-level configuration.
|
||||||
|
*
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @since 4.1
|
||||||
|
* @see SqlConfig
|
||||||
|
*/
|
||||||
|
class MergedSqlConfig {
|
||||||
|
|
||||||
|
private final String dataSource;
|
||||||
|
|
||||||
|
private final String transactionManager;
|
||||||
|
|
||||||
|
private final TransactionMode transactionMode;
|
||||||
|
|
||||||
|
private final String encoding;
|
||||||
|
|
||||||
|
private final String separator;
|
||||||
|
|
||||||
|
private final String commentPrefix;
|
||||||
|
|
||||||
|
private final String blockCommentStartDelimiter;
|
||||||
|
|
||||||
|
private final String blockCommentEndDelimiter;
|
||||||
|
|
||||||
|
private final ErrorMode errorMode;
|
||||||
|
|
||||||
|
|
||||||
|
private static TransactionMode retrieveTransactionMode(AnnotationAttributes attributes) {
|
||||||
|
TransactionMode transactionMode = attributes.getEnum("transactionMode");
|
||||||
|
if (transactionMode == TransactionMode.DEFAULT) {
|
||||||
|
transactionMode = TransactionMode.INFERRED;
|
||||||
|
}
|
||||||
|
return transactionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ErrorMode retrieveErrorMode(AnnotationAttributes attributes) {
|
||||||
|
ErrorMode errorMode = attributes.getEnum("errorMode");
|
||||||
|
if (errorMode == ErrorMode.DEFAULT) {
|
||||||
|
errorMode = ErrorMode.FAIL_ON_ERROR;
|
||||||
|
}
|
||||||
|
return errorMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String retrieveSeparator(AnnotationAttributes attributes) {
|
||||||
|
String separator = attributes.getString("separator");
|
||||||
|
if (separator != null && separator.equals("")) {
|
||||||
|
separator = ScriptUtils.DEFAULT_STATEMENT_SEPARATOR;
|
||||||
|
}
|
||||||
|
return separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String retrieveCommentPrefix(AnnotationAttributes attributes) {
|
||||||
|
String commentPrefix = attributes.getString("commentPrefix");
|
||||||
|
if (commentPrefix != null && commentPrefix.equals("")) {
|
||||||
|
commentPrefix = ScriptUtils.DEFAULT_COMMENT_PREFIX;
|
||||||
|
}
|
||||||
|
return commentPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String retrieveBlockCommentStartDelimiter(AnnotationAttributes attributes) {
|
||||||
|
String blockCommentStartDelimiter = attributes.getString("blockCommentStartDelimiter");
|
||||||
|
if (blockCommentStartDelimiter != null && blockCommentStartDelimiter.equals("")) {
|
||||||
|
blockCommentStartDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER;
|
||||||
|
}
|
||||||
|
return blockCommentStartDelimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String retrieveBlockCommentEndDelimiter(AnnotationAttributes attributes) {
|
||||||
|
String blockCommentEndDelimiter = attributes.getString("blockCommentEndDelimiter");
|
||||||
|
if (blockCommentEndDelimiter != null && blockCommentEndDelimiter.equals("")) {
|
||||||
|
blockCommentEndDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;
|
||||||
|
}
|
||||||
|
return blockCommentEndDelimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@code MergedSqlConfig} instance by merging the configuration
|
||||||
|
* from the supplied local (potentially method-level) {@code @SqlConfig} annotation
|
||||||
|
* with class-level configuration discovered on the supplied {@code testClass}.
|
||||||
|
* <p>Local configuration overrides class-level configuration.
|
||||||
|
* <p>If the test class is not annotated with {@code @SqlConfig}, no merging
|
||||||
|
* takes place and the local configuration is used "as is".
|
||||||
|
*/
|
||||||
|
MergedSqlConfig(SqlConfig localSqlConfig, Class<?> testClass) {
|
||||||
|
Assert.notNull(localSqlConfig, "Local @SqlConfig must not be null");
|
||||||
|
Assert.notNull(testClass, "testClass must not be null");
|
||||||
|
|
||||||
|
// Get global attributes, if any.
|
||||||
|
AnnotationAttributes attributes = AnnotatedElementUtils.getAnnotationAttributes(testClass,
|
||||||
|
SqlConfig.class.getName());
|
||||||
|
|
||||||
|
// Override global attributes with local attributes.
|
||||||
|
if (attributes != null) {
|
||||||
|
for (String key : attributes.keySet()) {
|
||||||
|
Object value = AnnotationUtils.getValue(localSqlConfig, key);
|
||||||
|
if (value != null) {
|
||||||
|
// Is the value explicit (i.e., not a 'default')?
|
||||||
|
if (!value.equals("") && (value != TransactionMode.DEFAULT) && (value != ErrorMode.DEFAULT)) {
|
||||||
|
attributes.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise, use local attributes only.
|
||||||
|
attributes = AnnotationUtils.getAnnotationAttributes(localSqlConfig, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataSource = attributes.getString("dataSource");
|
||||||
|
this.transactionManager = attributes.getString("transactionManager");
|
||||||
|
this.transactionMode = retrieveTransactionMode(attributes);
|
||||||
|
this.encoding = attributes.getString("encoding");
|
||||||
|
this.separator = retrieveSeparator(attributes);
|
||||||
|
this.commentPrefix = retrieveCommentPrefix(attributes);
|
||||||
|
this.blockCommentStartDelimiter = retrieveBlockCommentStartDelimiter(attributes);
|
||||||
|
this.blockCommentEndDelimiter = retrieveBlockCommentEndDelimiter(attributes);
|
||||||
|
this.errorMode = retrieveErrorMode(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#dataSource()
|
||||||
|
*/
|
||||||
|
String getDataSource() {
|
||||||
|
return this.dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#transactionManager()
|
||||||
|
*/
|
||||||
|
String getTransactionManager() {
|
||||||
|
return this.transactionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#transactionMode()
|
||||||
|
*/
|
||||||
|
TransactionMode getTransactionMode() {
|
||||||
|
return this.transactionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#encoding()
|
||||||
|
*/
|
||||||
|
String getEncoding() {
|
||||||
|
return this.encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#separator()
|
||||||
|
*/
|
||||||
|
String getSeparator() {
|
||||||
|
return this.separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#commentPrefix()
|
||||||
|
*/
|
||||||
|
String getCommentPrefix() {
|
||||||
|
return this.commentPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#blockCommentStartDelimiter()
|
||||||
|
*/
|
||||||
|
String getBlockCommentStartDelimiter() {
|
||||||
|
return this.blockCommentStartDelimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#blockCommentEndDelimiter()
|
||||||
|
*/
|
||||||
|
String getBlockCommentEndDelimiter() {
|
||||||
|
return this.blockCommentEndDelimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SqlConfig#errorMode()
|
||||||
|
*/
|
||||||
|
ErrorMode getErrorMode() {
|
||||||
|
return this.errorMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a String representation of the merged SQL script configuration.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringCreator(this)//
|
||||||
|
.append("dataSource", dataSource)//
|
||||||
|
.append("transactionManager", transactionManager)//
|
||||||
|
.append("transactionMode", transactionMode)//
|
||||||
|
.append("encoding", encoding)//
|
||||||
|
.append("separator", separator)//
|
||||||
|
.append("commentPrefix", commentPrefix)//
|
||||||
|
.append("blockCommentStartDelimiter", blockCommentStartDelimiter)//
|
||||||
|
.append("blockCommentEndDelimiter", blockCommentEndDelimiter)//
|
||||||
|
.append("errorMode", errorMode)//
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -37,12 +37,13 @@ import static java.lang.annotation.RetentionPolicy.*;
|
||||||
* <p>Script execution is performed by the {@link SqlScriptsTestExecutionListener},
|
* <p>Script execution is performed by the {@link SqlScriptsTestExecutionListener},
|
||||||
* which is enabled by default.
|
* which is enabled by default.
|
||||||
*
|
*
|
||||||
* <p>The configuration options provided by this annotation are equivalent to
|
* <p>The configuration options provided by this annotation and
|
||||||
* those supported by {@link ScriptUtils} and
|
* {@link SqlConfig @SqlConfig} are equivalent to those supported by
|
||||||
|
* {@link ScriptUtils} and
|
||||||
* {@link org.springframework.jdbc.datasource.init.ResourceDatabasePopulator}
|
* {@link org.springframework.jdbc.datasource.init.ResourceDatabasePopulator}
|
||||||
* but are a superset of those provided by the {@code <jdbc:initialize-database/>}
|
* but are a superset of those provided by the {@code <jdbc:initialize-database/>}
|
||||||
* XML namespace element. Consult the javadocs of individual attributes in this
|
* XML namespace element. Consult the javadocs of individual attributes in this
|
||||||
* annotation for details.
|
* annotation and {@link SqlConfig @SqlConfig} for details.
|
||||||
*
|
*
|
||||||
* <p>Beginning with Java 8, {@code @Sql} can be used as a
|
* <p>Beginning with Java 8, {@code @Sql} can be used as a
|
||||||
* <em>{@linkplain Repeatable repeatable}</em> annotation. Otherwise,
|
* <em>{@linkplain Repeatable repeatable}</em> annotation. Otherwise,
|
||||||
|
|
@ -55,7 +56,6 @@ import static java.lang.annotation.RetentionPolicy.*;
|
||||||
* meta-annotations.
|
* meta-annotations.
|
||||||
*
|
*
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @author Tadaya Tsuyukubo
|
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
* @see SqlGroup
|
* @see SqlGroup
|
||||||
* @see SqlScriptsTestExecutionListener
|
* @see SqlScriptsTestExecutionListener
|
||||||
|
|
@ -67,7 +67,7 @@ import static java.lang.annotation.RetentionPolicy.*;
|
||||||
@Documented
|
@Documented
|
||||||
@Inherited
|
@Inherited
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
@Target({TYPE, METHOD})
|
@Target({ TYPE, METHOD })
|
||||||
@Repeatable(SqlGroup.class)
|
@Repeatable(SqlGroup.class)
|
||||||
public @interface Sql {
|
public @interface Sql {
|
||||||
|
|
||||||
|
|
@ -130,114 +130,19 @@ public @interface Sql {
|
||||||
*/
|
*/
|
||||||
String[] scripts() default {};
|
String[] scripts() default {};
|
||||||
|
|
||||||
/**
|
|
||||||
* The encoding for the supplied SQL scripts, if different from the platform
|
|
||||||
* encoding.
|
|
||||||
* <p>An empty string denotes that the platform encoding should be used.
|
|
||||||
*/
|
|
||||||
String encoding() default "";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bean name of the {@link javax.sql.DataSource} against which the scripts
|
|
||||||
* should be executed.
|
|
||||||
* <p>The name is only used if there is more than one bean of type
|
|
||||||
* {@code DataSource} in the test's {@code ApplicationContext}. If there is
|
|
||||||
* only one such bean, it is not necessary to specify a bean name.
|
|
||||||
* <p>Defaults to an empty string, requiring that one of the following is
|
|
||||||
* true:
|
|
||||||
* <ol>
|
|
||||||
* <li>There is only one bean of type {@code DataSource} in the test's
|
|
||||||
* {@code ApplicationContext}.</li>
|
|
||||||
* <li>The {@code DataSource} to use is named {@code "dataSource"}.</li>
|
|
||||||
* </ol>
|
|
||||||
*/
|
|
||||||
String dataSource() default "";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
|
|
||||||
* PlatformTransactionManager} that should be used to drive transactions.
|
|
||||||
* <p>The name is only used if there is more than one bean of type
|
|
||||||
* {@code PlatformTransactionManager} in the test's {@code ApplicationContext}.
|
|
||||||
* If there is only one such bean, it is not necessary to specify a bean name.
|
|
||||||
* <p>Defaults to an empty string, requiring that one of the following is
|
|
||||||
* true:
|
|
||||||
* <ol>
|
|
||||||
* <li>There is only one bean of type {@code PlatformTransactionManager} in
|
|
||||||
* the test's {@code ApplicationContext}.</li>
|
|
||||||
* <li>{@link org.springframework.transaction.annotation.TransactionManagementConfigurer
|
|
||||||
* TransactionManagementConfigurer} has been implemented to specify which
|
|
||||||
* {@code PlatformTransactionManager} bean should be used for annotation-driven
|
|
||||||
* transaction management.</li>
|
|
||||||
* <li>The {@code PlatformTransactionManager} to use is named
|
|
||||||
* {@code "transactionManager"}.</li>
|
|
||||||
* </ol>
|
|
||||||
*/
|
|
||||||
String transactionManager() default "";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to indicate that the SQL scripts must be executed in a new transaction.
|
|
||||||
* <p>Defaults to {@code false}, meaning that the SQL scripts will be executed
|
|
||||||
* within the current transaction if present. The <em>current</em> transaction
|
|
||||||
* will typically be managed by the
|
|
||||||
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
|
||||||
* TransactionalTestExecutionListener}.
|
|
||||||
* <p>Can be set to {@code true} to ensure that the scripts are executed in
|
|
||||||
* a new, isolated transaction that will be immediately committed.
|
|
||||||
*/
|
|
||||||
boolean requireNewTransaction() default false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The character string used to separate individual statements within the
|
|
||||||
* SQL scripts.
|
|
||||||
* <p>Defaults to {@code ";"} if not specified and falls back to {@code "\n"}
|
|
||||||
* as a last resort; may be set to {@link ScriptUtils#EOF_STATEMENT_SEPARATOR}
|
|
||||||
* to signal that each script contains a single statement without a separator.
|
|
||||||
*/
|
|
||||||
String separator() default ScriptUtils.DEFAULT_STATEMENT_SEPARATOR;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The prefix that identifies single-line comments within the SQL scripts.
|
|
||||||
* <p>Defaults to {@code "--"}.
|
|
||||||
*/
|
|
||||||
String commentPrefix() default ScriptUtils.DEFAULT_COMMENT_PREFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The start delimiter that identifies block comments within the SQL scripts.
|
|
||||||
* <p>Defaults to {@code "/*"}.
|
|
||||||
* @see #blockCommentEndDelimiter
|
|
||||||
*/
|
|
||||||
String blockCommentStartDelimiter() default ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The end delimiter that identifies block comments within the SQL scripts.
|
|
||||||
* <p>Defaults to <code>"*/"</code>.
|
|
||||||
* @see #blockCommentStartDelimiter
|
|
||||||
*/
|
|
||||||
String blockCommentEndDelimiter() default ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to indicate that all failures in SQL should be logged but not cause
|
|
||||||
* a failure.
|
|
||||||
* <p>Defaults to {@code false}.
|
|
||||||
* @see #ignoreFailedDrops
|
|
||||||
*/
|
|
||||||
boolean continueOnError() default false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to indicate that a failed SQL {@code DROP} statement can be ignored.
|
|
||||||
* <p>This is useful for a non-embedded database whose SQL dialect does not
|
|
||||||
* support an {@code IF EXISTS} clause in a {@code DROP} statement.
|
|
||||||
* <p>The default is {@code false} so that if a script is accidentally
|
|
||||||
* executed, it will fail fast if the script starts with a {@code DROP}
|
|
||||||
* statement.
|
|
||||||
* @see #continueOnError
|
|
||||||
*/
|
|
||||||
boolean ignoreFailedDrops() default false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the SQL scripts should be executed.
|
* When the SQL scripts should be executed.
|
||||||
* <p>Defaults to {@link ExecutionPhase#BEFORE_TEST_METHOD BEFORE_TEST_METHOD}.
|
* <p>Defaults to {@link ExecutionPhase#BEFORE_TEST_METHOD BEFORE_TEST_METHOD}.
|
||||||
*/
|
*/
|
||||||
ExecutionPhase executionPhase() default ExecutionPhase.BEFORE_TEST_METHOD;
|
ExecutionPhase executionPhase() default ExecutionPhase.BEFORE_TEST_METHOD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local configuration for the SQL scripts declared within this
|
||||||
|
* {@code @Sql} annotation.
|
||||||
|
* <p>See the class-level javadocs for {@link SqlConfig} for explanations of
|
||||||
|
* local vs. global configuration, inheritance, overrides, etc.
|
||||||
|
* <p>Defaults to an empty {@link SqlConfig @SqlConfig} instance.
|
||||||
|
*/
|
||||||
|
SqlConfig config() default @SqlConfig();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2014 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.test.context.jdbc;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.springframework.jdbc.datasource.init.ScriptUtils;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code @SqlConfig} defines metadata that is used to determine how to parse
|
||||||
|
* and execute SQL scripts configured via the {@link Sql @Sql} annotation.
|
||||||
|
*
|
||||||
|
* <h3>Configuration Scope</h3>
|
||||||
|
* <p>When declared as a class-level annotation on an integration test class,
|
||||||
|
* {@code @SqlConfig} serves as <strong><em>global</em></strong> configuration
|
||||||
|
* for all SQL scripts within the test class hierarchy. When declared directly
|
||||||
|
* via the {@link Sql#config config} attribute of the {@code @Sql} annotation,
|
||||||
|
* {@code @SqlConfig} serves as <strong><em>local</em></strong> configuration
|
||||||
|
* for the SQL scripts declared within the enclosing {@code @Sql} annotation.
|
||||||
|
*
|
||||||
|
* <h3>Default Values</h3>
|
||||||
|
* <p>Every attribute in {@code @SqlConfig} has an <em>implicit</em> default value
|
||||||
|
* which is documented in the javadocs of the corresponding attribute. Due to the
|
||||||
|
* rules defined for annotation attributes in the Java Language Specification, it
|
||||||
|
* is unfortunately not possible to assign a value of {@code null} to an annotation
|
||||||
|
* attribute. Thus, in order to support overrides of <em>inherited</em> global
|
||||||
|
* configuration, {@code @SqlConfig} attributes have an <em>explicit</em>
|
||||||
|
* {@code default} value of either {@code ""} for Strings or {@code DEFAULT} for
|
||||||
|
* Enums. This approach allows local declarations {@code @SqlConfig} to
|
||||||
|
* selectively override individual attributes from global declarations of
|
||||||
|
* {@code @SqlConfig} by providing a value other than {@code ""} or {@code DEFAULT}.
|
||||||
|
*
|
||||||
|
* <h3>Inheritance and Overrides</h3>
|
||||||
|
* <p>Global {@code @SqlConfig} attributes are <em>inherited</em> whenever local
|
||||||
|
* {@code @SqlConfig} attributes do not supply an explicit value other than
|
||||||
|
* {@code ""} or {@code DEFAULT}. Explicit local configuration therefore
|
||||||
|
* <em>overrides</em> global configuration.
|
||||||
|
*
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @author Tadaya Tsuyukubo
|
||||||
|
* @since 4.1
|
||||||
|
* @see Sql
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Target(TYPE)
|
||||||
|
public @interface SqlConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration of <em>modes</em> that dictate whether SQL scripts should be
|
||||||
|
* executed within a transaction and what the transaction propagation behavior
|
||||||
|
* should be.
|
||||||
|
*/
|
||||||
|
static enum TransactionMode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the <em>default</em> transaction mode should be used.
|
||||||
|
* <p>The meaning of <em>default</em> depends on the context in which
|
||||||
|
* {@code @SqlConfig} is declared:
|
||||||
|
* <ul>
|
||||||
|
* <li>If {@code @SqlConfig} is declared <strong>only</strong> locally,
|
||||||
|
* the default transaction mode is {@link #INFERRED}.</li>
|
||||||
|
* <li>If {@code @SqlConfig} is declared globally, the default transaction
|
||||||
|
* mode is {@link #INFERRED}.</li>
|
||||||
|
* <li>If {@code @SqlConfig} is declared globally <strong>and</strong>
|
||||||
|
* locally, the default transaction mode for the local declaration is
|
||||||
|
* inherited from the global declaration.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the transaction mode to use when executing SQL scripts
|
||||||
|
* should be <em>inferred</em> based on whether or not a Spring-managed
|
||||||
|
* transaction is currently present.
|
||||||
|
* <p>SQL scripts will be executed within the current transaction if present;
|
||||||
|
* otherwise, scripts will be executed in a new transaction that will be
|
||||||
|
* immediately committed.
|
||||||
|
* <p>The <em>current</em> transaction will typically be managed by the
|
||||||
|
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||||
|
* TransactionalTestExecutionListener}.
|
||||||
|
* @see #ISOLATED
|
||||||
|
*/
|
||||||
|
INFERRED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that SQL scripts should always be executed in a new,
|
||||||
|
* <em>isolated</em> transaction that will be immediately committed.
|
||||||
|
*/
|
||||||
|
ISOLATED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration of <em>modes</em> that dictate how errors are handled while
|
||||||
|
* executing SQL statements.
|
||||||
|
*/
|
||||||
|
static enum ErrorMode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the <em>default</em> error mode should be used.
|
||||||
|
* <p>The meaning of <em>default</em> depends on the context in which
|
||||||
|
* {@code @SqlConfig} is declared:
|
||||||
|
* <ul>
|
||||||
|
* <li>If {@code @SqlConfig} is declared <strong>only</strong> locally,
|
||||||
|
* the default error mode is {@link #FAIL_ON_ERROR}.</li>
|
||||||
|
* <li>If {@code @SqlConfig} is declared globally, the default error
|
||||||
|
* mode is {@link #FAIL_ON_ERROR}.</li>
|
||||||
|
* <li>If {@code @SqlConfig} is declared globally <strong>and</strong>
|
||||||
|
* locally, the default error mode for the local declaration is
|
||||||
|
* inherited from the global declaration.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that script execution will fail if an error is encountered.
|
||||||
|
* In other words, no errors should be ignored.
|
||||||
|
* <p>This is effectively the default error mode so that if a script
|
||||||
|
* is accidentally executed, it will fail fast if any SQL statement in
|
||||||
|
* the script results in an error.
|
||||||
|
* @see #CONTINUE_ON_ERROR
|
||||||
|
*/
|
||||||
|
FAIL_ON_ERROR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that all errors in SQL scripts should be logged but not
|
||||||
|
* propagated as exceptions.
|
||||||
|
* <p>{@code CONTINUE_ON_ERROR} is the logical <em>opposite</em> of
|
||||||
|
* {@code FAIL_ON_ERROR} and a <em>superset</em> of {@code IGNORE_FAILED_DROPS}.
|
||||||
|
* @see #FAIL_ON_ERROR
|
||||||
|
* @see #IGNORE_FAILED_DROPS
|
||||||
|
*/
|
||||||
|
CONTINUE_ON_ERROR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that failed SQL {@code DROP} statements can be ignored.
|
||||||
|
* <p>This is useful for a non-embedded database whose SQL dialect does
|
||||||
|
* not support an {@code IF EXISTS} clause in a {@code DROP} statement.
|
||||||
|
* @see #CONTINUE_ON_ERROR
|
||||||
|
*/
|
||||||
|
IGNORE_FAILED_DROPS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bean name of the {@link javax.sql.DataSource} against which the scripts
|
||||||
|
* should be executed.
|
||||||
|
* <p>The name is only used if there is more than one bean of type
|
||||||
|
* {@code DataSource} in the test's {@code ApplicationContext}. If there is
|
||||||
|
* only one such bean, it is not necessary to specify a bean name.
|
||||||
|
* <p>Defaults to an empty string, requiring that one of the following is
|
||||||
|
* true:
|
||||||
|
* <ol>
|
||||||
|
* <li>There is only one bean of type {@code DataSource} in the test's
|
||||||
|
* {@code ApplicationContext}.</li>
|
||||||
|
* <li>The {@code DataSource} to use is named {@code "dataSource"}.</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
String dataSource() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
|
||||||
|
* PlatformTransactionManager} that should be used to drive transactions.
|
||||||
|
* <p>The name is only used if there is more than one bean of type
|
||||||
|
* {@code PlatformTransactionManager} in the test's {@code ApplicationContext}.
|
||||||
|
* If there is only one such bean, it is not necessary to specify a bean name.
|
||||||
|
* <p>Defaults to an empty string, requiring that one of the following is
|
||||||
|
* true:
|
||||||
|
* <ol>
|
||||||
|
* <li>There is only one bean of type {@code PlatformTransactionManager} in
|
||||||
|
* the test's {@code ApplicationContext}.</li>
|
||||||
|
* <li>{@link org.springframework.transaction.annotation.TransactionManagementConfigurer
|
||||||
|
* TransactionManagementConfigurer} has been implemented to specify which
|
||||||
|
* {@code PlatformTransactionManager} bean should be used for annotation-driven
|
||||||
|
* transaction management.</li>
|
||||||
|
* <li>The {@code PlatformTransactionManager} to use is named
|
||||||
|
* {@code "transactionManager"}.</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
String transactionManager() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <em>mode</em> to use when determining whether SQL scripts should be
|
||||||
|
* executed within a transaction.
|
||||||
|
* <p>Defaults to {@link TransactionMode#DEFAULT DEFAULT}.
|
||||||
|
* <p>Can be set to {@link TransactionMode#ISOLATED} to ensure that the SQL
|
||||||
|
* scripts are executed in a new, isolated transaction that will be immediately
|
||||||
|
* committed.
|
||||||
|
* @see TransactionMode
|
||||||
|
*/
|
||||||
|
TransactionMode transactionMode() default TransactionMode.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The encoding for the supplied SQL scripts, if different from the platform
|
||||||
|
* encoding.
|
||||||
|
* <p>An empty string denotes that the platform encoding should be used.
|
||||||
|
*/
|
||||||
|
String encoding() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The character string used to separate individual statements within the
|
||||||
|
* SQL scripts.
|
||||||
|
* <p>Implicitly defaults to {@code ";"} if not specified and falls back to
|
||||||
|
* {@code "\n"} as a last resort.
|
||||||
|
* <p>May be set to {@link ScriptUtils#EOF_STATEMENT_SEPARATOR} to signal
|
||||||
|
* that each script contains a single statement without a separator.
|
||||||
|
* @see ScriptUtils#DEFAULT_STATEMENT_SEPARATOR
|
||||||
|
*/
|
||||||
|
String separator() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix that identifies single-line comments within the SQL scripts.
|
||||||
|
* <p>Implicitly defaults to {@code "--"}.
|
||||||
|
* @see ScriptUtils#DEFAULT_COMMENT_PREFIX
|
||||||
|
*/
|
||||||
|
String commentPrefix() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start delimiter that identifies block comments within the SQL scripts.
|
||||||
|
* <p>Implicitly defaults to {@code "/*"}.
|
||||||
|
* @see #blockCommentEndDelimiter
|
||||||
|
* @see ScriptUtils#DEFAULT_BLOCK_COMMENT_START_DELIMITER
|
||||||
|
*/
|
||||||
|
String blockCommentStartDelimiter() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end delimiter that identifies block comments within the SQL scripts.
|
||||||
|
* <p>Implicitly defaults to <code>"*/"</code>.
|
||||||
|
* @see #blockCommentStartDelimiter
|
||||||
|
* @see ScriptUtils#DEFAULT_BLOCK_COMMENT_END_DELIMITER
|
||||||
|
*/
|
||||||
|
String blockCommentEndDelimiter() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <em>mode</em> to use when an error is encountered while executing an
|
||||||
|
* SQL statement.
|
||||||
|
* <p>Defaults to {@link ErrorMode#DEFAULT DEFAULT}.
|
||||||
|
* @see ErrorMode
|
||||||
|
*/
|
||||||
|
ErrorMode errorMode() default ErrorMode.DEFAULT;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,8 @@ import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||||
import org.springframework.test.context.TestContext;
|
import org.springframework.test.context.TestContext;
|
||||||
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||||
|
import org.springframework.test.context.jdbc.SqlConfig.ErrorMode;
|
||||||
|
import org.springframework.test.context.jdbc.SqlConfig.TransactionMode;
|
||||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||||
import org.springframework.test.context.transaction.TestContextTransactionUtils;
|
import org.springframework.test.context.transaction.TestContextTransactionUtils;
|
||||||
import org.springframework.test.context.util.TestContextResourceUtils;
|
import org.springframework.test.context.util.TestContextResourceUtils;
|
||||||
|
|
@ -138,23 +140,24 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
private void executeSqlScripts(Sql sql, ExecutionPhase executionPhase, TestContext testContext, boolean classLevel)
|
private void executeSqlScripts(Sql sql, ExecutionPhase executionPhase, TestContext testContext, boolean classLevel)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug(String.format("Processing %s for execution phase [%s] and test context %s.", sql,
|
|
||||||
executionPhase, testContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executionPhase != sql.executionPhase()) {
|
if (executionPhase != sql.executionPhase()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MergedSqlConfig mergedSqlConfig = new MergedSqlConfig(sql.config(), testContext.getTestClass());
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(String.format("Processing %s for execution phase [%s] and test context %s.", mergedSqlConfig,
|
||||||
|
executionPhase, testContext));
|
||||||
|
}
|
||||||
|
|
||||||
final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||||
populator.setSqlScriptEncoding(sql.encoding());
|
populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
|
||||||
populator.setSeparator(sql.separator());
|
populator.setSeparator(mergedSqlConfig.getSeparator());
|
||||||
populator.setCommentPrefix(sql.commentPrefix());
|
populator.setCommentPrefix(mergedSqlConfig.getCommentPrefix());
|
||||||
populator.setBlockCommentStartDelimiter(sql.blockCommentStartDelimiter());
|
populator.setBlockCommentStartDelimiter(mergedSqlConfig.getBlockCommentStartDelimiter());
|
||||||
populator.setBlockCommentEndDelimiter(sql.blockCommentEndDelimiter());
|
populator.setBlockCommentEndDelimiter(mergedSqlConfig.getBlockCommentEndDelimiter());
|
||||||
populator.setContinueOnError(sql.continueOnError());
|
populator.setContinueOnError(mergedSqlConfig.getErrorMode() == ErrorMode.CONTINUE_ON_ERROR);
|
||||||
populator.setIgnoreFailedDrops(sql.ignoreFailedDrops());
|
populator.setIgnoreFailedDrops(mergedSqlConfig.getErrorMode() == ErrorMode.IGNORE_FAILED_DROPS);
|
||||||
|
|
||||||
String[] scripts = getScripts(sql, testContext, classLevel);
|
String[] scripts = getScripts(sql, testContext, classLevel);
|
||||||
scripts = TestContextResourceUtils.convertToClasspathResourcePaths(testContext.getTestClass(), scripts);
|
scripts = TestContextResourceUtils.convertToClasspathResourcePaths(testContext.getTestClass(), scripts);
|
||||||
|
|
@ -163,11 +166,12 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
|
||||||
logger.debug("Executing SQL scripts: " + ObjectUtils.nullSafeToString(scripts));
|
logger.debug("Executing SQL scripts: " + ObjectUtils.nullSafeToString(scripts));
|
||||||
}
|
}
|
||||||
|
|
||||||
final DataSource dataSource = TestContextTransactionUtils.retrieveDataSource(testContext, sql.dataSource());
|
final DataSource dataSource = TestContextTransactionUtils.retrieveDataSource(testContext,
|
||||||
|
mergedSqlConfig.getDataSource());
|
||||||
final PlatformTransactionManager transactionManager = TestContextTransactionUtils.retrieveTransactionManager(
|
final PlatformTransactionManager transactionManager = TestContextTransactionUtils.retrieveTransactionManager(
|
||||||
testContext, sql.transactionManager());
|
testContext, mergedSqlConfig.getTransactionManager());
|
||||||
|
|
||||||
int propagation = sql.requireNewTransaction() ? TransactionDefinition.PROPAGATION_REQUIRES_NEW
|
int propagation = (mergedSqlConfig.getTransactionMode() == TransactionMode.ISOLATED) ? TransactionDefinition.PROPAGATION_REQUIRES_NEW
|
||||||
: TransactionDefinition.PROPAGATION_REQUIRED;
|
: TransactionDefinition.PROPAGATION_REQUIRED;
|
||||||
|
|
||||||
TransactionAttribute transactionAttribute = TestContextTransactionUtils.createDelegatingTransactionAttribute(
|
TransactionAttribute transactionAttribute = TestContextTransactionUtils.createDelegatingTransactionAttribute(
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests that verify support for custom SQL script syntax
|
* Integration tests that verify support for custom SQL script syntax
|
||||||
* configured via {@link Sql @Sql}.
|
* configured via {@link SqlConfig @SqlConfig}.
|
||||||
*
|
*
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
|
@ -35,16 +35,9 @@ import static org.junit.Assert.*;
|
||||||
public class CustomScriptSyntaxSqlScriptsTests extends AbstractTransactionalJUnit4SpringContextTests {
|
public class CustomScriptSyntaxSqlScriptsTests extends AbstractTransactionalJUnit4SpringContextTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SqlGroup({//
|
@Sql("schema.sql")
|
||||||
@Sql("schema.sql"),//
|
@Sql(scripts = "data-add-users-with-custom-script-syntax.sql",//
|
||||||
@Sql(//
|
config = @SqlConfig(commentPrefix = "`", blockCommentStartDelimiter = "#$", blockCommentEndDelimiter = "$#", separator = "@@"))
|
||||||
scripts = "data-add-users-with-custom-script-syntax.sql",//
|
|
||||||
commentPrefix = "`",//
|
|
||||||
blockCommentStartDelimiter = "#$",//
|
|
||||||
blockCommentEndDelimiter = "$#",//
|
|
||||||
separator = "@@"//
|
|
||||||
) //
|
|
||||||
})
|
|
||||||
public void methodLevelScripts() {
|
public void methodLevelScripts() {
|
||||||
assertNumUsers(3);
|
assertNumUsers(3);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2014 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.test.context.jdbc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modified copy of {@link CustomScriptSyntaxSqlScriptsTests} with
|
||||||
|
* {@link SqlConfig @SqlConfig} defined at the class level.
|
||||||
|
*
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
@ContextConfiguration(classes = EmptyDatabaseConfig.class)
|
||||||
|
@DirtiesContext
|
||||||
|
@SqlConfig(commentPrefix = "`", blockCommentStartDelimiter = "#$", blockCommentEndDelimiter = "$#", separator = "@@")
|
||||||
|
public class GlobalCustomScriptSyntaxSqlScriptsTests extends AbstractTransactionalJUnit4SpringContextTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Sql(scripts = "schema.sql", config = @SqlConfig(separator = ";"))
|
||||||
|
@Sql("data-add-users-with-custom-script-syntax.sql")
|
||||||
|
public void methodLevelScripts() {
|
||||||
|
assertNumUsers(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertNumUsers(int expected) {
|
||||||
|
assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2014 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.test.context.jdbc;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.springframework.jdbc.datasource.init.ScriptUtils.*;
|
||||||
|
import static org.springframework.test.context.jdbc.SqlConfig.ErrorMode.*;
|
||||||
|
import static org.springframework.test.context.jdbc.SqlConfig.TransactionMode.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link MergedSqlConfig}.
|
||||||
|
*
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public class MergedSqlConfigTests {
|
||||||
|
|
||||||
|
private void assertDefaults(MergedSqlConfig cfg) {
|
||||||
|
assertNotNull(cfg);
|
||||||
|
assertEquals("dataSource", "", cfg.getDataSource());
|
||||||
|
assertEquals("transactionManager", "", cfg.getTransactionManager());
|
||||||
|
assertEquals("transactionMode", INFERRED, cfg.getTransactionMode());
|
||||||
|
assertEquals("encoding", "", cfg.getEncoding());
|
||||||
|
assertEquals("separator", DEFAULT_STATEMENT_SEPARATOR, cfg.getSeparator());
|
||||||
|
assertEquals("commentPrefix", DEFAULT_COMMENT_PREFIX, cfg.getCommentPrefix());
|
||||||
|
assertEquals("blockCommentStartDelimiter", DEFAULT_BLOCK_COMMENT_START_DELIMITER,
|
||||||
|
cfg.getBlockCommentStartDelimiter());
|
||||||
|
assertEquals("blockCommentEndDelimiter", DEFAULT_BLOCK_COMMENT_END_DELIMITER, cfg.getBlockCommentEndDelimiter());
|
||||||
|
assertEquals("errorMode", FAIL_ON_ERROR, cfg.getErrorMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void localConfigWithDefaults() throws Exception {
|
||||||
|
Method method = getClass().getMethod("localConfigMethodWithDefaults");
|
||||||
|
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
|
||||||
|
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
|
||||||
|
assertDefaults(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void globalConfigWithDefaults() throws Exception {
|
||||||
|
Method method = GlobalConfigWithDefaultsClass.class.getMethod("globalConfigMethod");
|
||||||
|
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
|
||||||
|
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigWithDefaultsClass.class);
|
||||||
|
assertDefaults(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void localConfigWithCustomValues() throws Exception {
|
||||||
|
Method method = getClass().getMethod("localConfigMethodWithCustomValues");
|
||||||
|
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
|
||||||
|
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
|
||||||
|
assertNotNull(cfg);
|
||||||
|
assertEquals("dataSource", "ds", cfg.getDataSource());
|
||||||
|
assertEquals("transactionManager", "txMgr", cfg.getTransactionManager());
|
||||||
|
assertEquals("transactionMode", ISOLATED, cfg.getTransactionMode());
|
||||||
|
assertEquals("encoding", "enigma", cfg.getEncoding());
|
||||||
|
assertEquals("separator", "\n", cfg.getSeparator());
|
||||||
|
assertEquals("commentPrefix", "`", cfg.getCommentPrefix());
|
||||||
|
assertEquals("blockCommentStartDelimiter", "<<", cfg.getBlockCommentStartDelimiter());
|
||||||
|
assertEquals("blockCommentEndDelimiter", ">>", cfg.getBlockCommentEndDelimiter());
|
||||||
|
assertEquals("errorMode", IGNORE_FAILED_DROPS, cfg.getErrorMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void localConfigWithContinueOnError() throws Exception {
|
||||||
|
Method method = getClass().getMethod("localConfigMethodWithContinueOnError");
|
||||||
|
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
|
||||||
|
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
|
||||||
|
assertNotNull(cfg);
|
||||||
|
assertEquals("errorMode", CONTINUE_ON_ERROR, cfg.getErrorMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void localConfigWithIgnoreFailedDrops() throws Exception {
|
||||||
|
Method method = getClass().getMethod("localConfigMethodWithIgnoreFailedDrops");
|
||||||
|
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
|
||||||
|
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
|
||||||
|
assertNotNull(cfg);
|
||||||
|
assertEquals("errorMode", IGNORE_FAILED_DROPS, cfg.getErrorMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void globalConfig() throws Exception {
|
||||||
|
Method method = GlobalConfigClass.class.getMethod("globalConfigMethod");
|
||||||
|
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
|
||||||
|
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigClass.class);
|
||||||
|
assertNotNull(cfg);
|
||||||
|
assertEquals("dataSource", "", cfg.getDataSource());
|
||||||
|
assertEquals("transactionManager", "", cfg.getTransactionManager());
|
||||||
|
assertEquals("transactionMode", INFERRED, cfg.getTransactionMode());
|
||||||
|
assertEquals("encoding", "global", cfg.getEncoding());
|
||||||
|
assertEquals("separator", "\n", cfg.getSeparator());
|
||||||
|
assertEquals("commentPrefix", DEFAULT_COMMENT_PREFIX, cfg.getCommentPrefix());
|
||||||
|
assertEquals("blockCommentStartDelimiter", DEFAULT_BLOCK_COMMENT_START_DELIMITER,
|
||||||
|
cfg.getBlockCommentStartDelimiter());
|
||||||
|
assertEquals("blockCommentEndDelimiter", DEFAULT_BLOCK_COMMENT_END_DELIMITER, cfg.getBlockCommentEndDelimiter());
|
||||||
|
assertEquals("errorMode", IGNORE_FAILED_DROPS, cfg.getErrorMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void globalConfigWithLocalOverrides() throws Exception {
|
||||||
|
Method method = GlobalConfigClass.class.getMethod("globalConfigWithLocalOverridesMethod");
|
||||||
|
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
|
||||||
|
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigClass.class);
|
||||||
|
|
||||||
|
assertNotNull(cfg);
|
||||||
|
assertEquals("dataSource", "", cfg.getDataSource());
|
||||||
|
assertEquals("transactionManager", "", cfg.getTransactionManager());
|
||||||
|
assertEquals("transactionMode", INFERRED, cfg.getTransactionMode());
|
||||||
|
assertEquals("encoding", "local", cfg.getEncoding());
|
||||||
|
assertEquals("separator", "@@", cfg.getSeparator());
|
||||||
|
assertEquals("commentPrefix", DEFAULT_COMMENT_PREFIX, cfg.getCommentPrefix());
|
||||||
|
assertEquals("blockCommentStartDelimiter", DEFAULT_BLOCK_COMMENT_START_DELIMITER,
|
||||||
|
cfg.getBlockCommentStartDelimiter());
|
||||||
|
assertEquals("blockCommentEndDelimiter", DEFAULT_BLOCK_COMMENT_END_DELIMITER, cfg.getBlockCommentEndDelimiter());
|
||||||
|
assertEquals("errorMode", CONTINUE_ON_ERROR, cfg.getErrorMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Sql
|
||||||
|
public static void localConfigMethodWithDefaults() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Sql(config = @SqlConfig(dataSource = "ds", transactionManager = "txMgr", transactionMode = ISOLATED, encoding = "enigma", separator = "\n", commentPrefix = "`", blockCommentStartDelimiter = "<<", blockCommentEndDelimiter = ">>", errorMode = IGNORE_FAILED_DROPS))
|
||||||
|
public static void localConfigMethodWithCustomValues() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Sql(config = @SqlConfig(errorMode = CONTINUE_ON_ERROR))
|
||||||
|
public static void localConfigMethodWithContinueOnError() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Sql(config = @SqlConfig(errorMode = IGNORE_FAILED_DROPS))
|
||||||
|
public static void localConfigMethodWithIgnoreFailedDrops() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SqlConfig
|
||||||
|
public static class GlobalConfigWithDefaultsClass {
|
||||||
|
|
||||||
|
@Sql("foo.sql")
|
||||||
|
public void globalConfigMethod() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SqlConfig(encoding = "global", separator = "\n", errorMode = IGNORE_FAILED_DROPS)
|
||||||
|
public static class GlobalConfigClass {
|
||||||
|
|
||||||
|
@Sql("foo.sql")
|
||||||
|
public void globalConfigMethod() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Sql(scripts = "foo.sql", config = @SqlConfig(encoding = "local", separator = "@@", errorMode = CONTINUE_ON_ERROR))
|
||||||
|
public void globalConfigWithLocalOverridesMethod() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,7 @@ import static org.junit.Assert.*;
|
||||||
/**
|
/**
|
||||||
* Integration tests for {@link Sql @Sql} that verify support for multiple
|
* Integration tests for {@link Sql @Sql} that verify support for multiple
|
||||||
* {@link DataSource}s and {@link PlatformTransactionManager}s.
|
* {@link DataSource}s and {@link PlatformTransactionManager}s.
|
||||||
|
* <p>Simultaneously tests for method-level overrides via {@code @SqlConfig}.
|
||||||
*
|
*
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
|
@ -48,6 +49,7 @@ import static org.junit.Assert.*;
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@ContextConfiguration
|
@ContextConfiguration
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
|
@SqlConfig(dataSource = "dataSource1", transactionManager = "txMgr1")
|
||||||
public class MultipleDataSourcesAndTransactionManagersSqlScriptsTests {
|
public class MultipleDataSourcesAndTransactionManagersSqlScriptsTests {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
@ -58,13 +60,13 @@ public class MultipleDataSourcesAndTransactionManagersSqlScriptsTests {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Sql(scripts = "data-add-dogbert.sql", dataSource = "dataSource1", transactionManager = "txMgr1")
|
@Sql("data-add-dogbert.sql")
|
||||||
public void database1() {
|
public void database1() {
|
||||||
assertUsers(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
|
assertUsers(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Sql(scripts = "data-add-catbert.sql", dataSource = "dataSource2", transactionManager = "txMgr2")
|
@Sql(scripts = "data-add-catbert.sql", config = @SqlConfig(dataSource = "dataSource2", transactionManager = "txMgr2"))
|
||||||
public void database2() {
|
public void database2() {
|
||||||
assertUsers(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
|
assertUsers(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ import static org.junit.Assert.*;
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@ContextConfiguration
|
@ContextConfiguration
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
|
@Transactional("txMgr1")
|
||||||
|
@SqlConfig(dataSource = "dataSource1", transactionManager = "txMgr1")
|
||||||
public class MultipleDataSourcesAndTransactionManagersTransactionalSqlScriptsTests {
|
public class MultipleDataSourcesAndTransactionManagersTransactionalSqlScriptsTests {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
@ -58,15 +60,14 @@ public class MultipleDataSourcesAndTransactionManagersTransactionalSqlScriptsTes
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional("txMgr1")
|
@Sql("data-add-dogbert.sql")
|
||||||
@Sql(scripts = "data-add-dogbert.sql", dataSource = "dataSource1", transactionManager = "txMgr1")
|
|
||||||
public void database1() {
|
public void database1() {
|
||||||
assertUsers(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
|
assertUsers(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional("txMgr2")
|
@Transactional("txMgr2")
|
||||||
@Sql(scripts = "data-add-catbert.sql", dataSource = "dataSource2", transactionManager = "txMgr2")
|
@Sql(scripts = "data-add-catbert.sql", config = @SqlConfig(dataSource = "dataSource2", transactionManager = "txMgr2"))
|
||||||
public void database2() {
|
public void database2() {
|
||||||
assertUsers(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
|
assertUsers(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package org.springframework.test.context.jdbc;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.jdbc.SqlConfig.TransactionMode;
|
||||||
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
|
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
|
||||||
import org.springframework.test.context.transaction.AfterTransaction;
|
import org.springframework.test.context.transaction.AfterTransaction;
|
||||||
import org.springframework.test.context.transaction.BeforeTransaction;
|
import org.springframework.test.context.transaction.BeforeTransaction;
|
||||||
|
|
@ -27,7 +28,7 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transactional integration tests that verify commit semantics for
|
* Transactional integration tests that verify commit semantics for
|
||||||
* {@link Sql#requireNewTransaction}.
|
* {@link SqlConfig#requireNewTransaction}.
|
||||||
*
|
*
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
|
@ -42,7 +43,7 @@ public class RequiresNewTransactionSqlScriptsTests extends AbstractTransactional
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SqlGroup(@Sql(scripts = "data-add-dogbert.sql", requireNewTransaction = true))
|
@SqlGroup(@Sql(scripts = "data-add-dogbert.sql", config = @SqlConfig(transactionMode = TransactionMode.ISOLATED)))
|
||||||
public void methodLevelScripts() {
|
public void methodLevelScripts() {
|
||||||
assertNumUsers(1);
|
assertNumUsers(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public class TransactionalAfterTestMethodSqlScriptsTests extends AbstractTransac
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SqlGroup({//
|
@SqlGroup({//
|
||||||
@Sql({ "schema.sql", "data.sql" }),//
|
@Sql({ "schema.sql", "data.sql" }),//
|
||||||
@Sql(scripts = "drop-schema.sql", executionPhase = AFTER_TEST_METHOD) //
|
@Sql(scripts = "drop-schema.sql", executionPhase = AFTER_TEST_METHOD) //
|
||||||
})
|
})
|
||||||
// test## is required for @FixMethodOrder.
|
// test## is required for @FixMethodOrder.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue