diff --git a/spring-test-dbunit-sample/pom.xml b/spring-test-dbunit-sample/pom.xml index 46336c7a..d9c363bf 100644 --- a/spring-test-dbunit-sample/pom.xml +++ b/spring-test-dbunit-sample/pom.xml @@ -92,7 +92,7 @@ com.github.springtestdbunit spring-test-dbunit - 1.0.1-SNAPSHOT + 1.0.2-SNAPSHOT test diff --git a/spring-test-dbunit/src/main/java/com/github/springtestdbunit/DbUnitRunner.java b/spring-test-dbunit/src/main/java/com/github/springtestdbunit/DbUnitRunner.java index bbe15c30..77f057df 100644 --- a/spring-test-dbunit/src/main/java/com/github/springtestdbunit/DbUnitRunner.java +++ b/spring-test-dbunit/src/main/java/com/github/springtestdbunit/DbUnitRunner.java @@ -16,15 +16,22 @@ package com.github.springtestdbunit; import java.lang.annotation.Annotation; +import java.sql.ResultSet; +import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dbunit.database.IDatabaseConnection; +import org.dbunit.dataset.Column; +import org.dbunit.dataset.DefaultTable; import org.dbunit.dataset.IDataSet; +import org.dbunit.dataset.ITable; +import org.dbunit.dataset.ITableIterator; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -37,8 +44,8 @@ import com.github.springtestdbunit.dataset.DataSetLoader; /** - * Internal delegate class used to run tests with support for {@link DatabaseSetup @DatabaseSetup}, - * {@link DatabaseTearDown @DatabaseTearDown} and {@link ExpectedDatabase @ExpectedDatabase} annotations. + * Internal delegate class used to run tests with support for {@link com.github.springtestdbunit.annotation.DatabaseSetup @DatabaseSetup}, + * {@link com.github.springtestdbunit.annotation.DatabaseTearDown @DatabaseTearDown} and {@link com.github.springtestdbunit.annotation.ExpectedDatabase @ExpectedDatabase} annotations. * * @author Phillip Webb * @author Mario Zagar @@ -106,7 +113,7 @@ private void verifyExpected(DbUnitTestContext testContext, Collection> sequences = new HashMap>(); + + // We iterate on the lines and columns + for (int i = 0; i < table.getRowCount(); i++) { + for (Column column : cols) { + + // We verify that the value is not null + Object o = table.getValue(i, column.getColumnName()); + if (o != null) { + + // We look for the expression "${...}" in the value + String value = o.toString(); + if (value.startsWith("${") && value.endsWith("}")) { + + // We retrieve the sequence name + String key = value.substring(2, value.length() - 1); + + // if the map does not contain the list corresponding to the key we create it + if (sequences.containsKey(key) == false) { + sequences.put(key, new ArrayList()); + } + + // we stock the data of the element we just found + List liste = sequences.get(key); + TableValue e = new TableValue(i, column.getColumnName()); + liste.add(e); + } + } + } + } + + // we iterate the sequence names and we replace them in the dataset with the index + // that it should have according to number of times we found it in the dataset + for (String seq : sequences.keySet()) { + List liste = sequences.get(seq); + long nextVal = getNextSequenceValue(seq, databaseConnection); + long idValue = nextVal - liste.size(); + + for (TableValue e : liste) { + DefaultTable dt = (DefaultTable) table; + dt.setValue(e.getRow(), e.getColumnName(), idValue); + idValue++; + } + } + } + } + + /** + * Retrieves the next value in a SQL Sequence. + * + * @param sequenceName + * @param databaseConnection + * @return + * @throws Exception + */ + private long getNextSequenceValue(String sequenceName, IDatabaseConnection databaseConnection) throws Exception { + Statement st = databaseConnection.getConnection().createStatement(); + ResultSet rs = st.executeQuery("SELECT " + sequenceName + ".nextval FROM dual"); + rs.next(); + return rs.getLong(1); + } + private void setupOrTeardown(DbUnitTestContext testContext, boolean isSetup, Collection annotations) throws Exception { IDatabaseConnection connection = testContext.getConnection(); @@ -191,4 +289,33 @@ public static Collection get(Collec return annotationAttributes; } } + + /** + * Utility class to stock the row and columnName of the elements in the DataSet in which there is an Oracle Sequence + * to replace. + * + * @author cachavezley + */ + private class TableValue { + private final int row; + private final String columnName; + + private TableValue(int row, String columnName) { + this.row = row; + this.columnName = columnName; + } + + private int getRow() { + return this.row; + } + + private String getColumnName() { + return this.columnName; + } + + @Override + public String toString() { + return "TableValue [row=" + this.row + ", columnName=" + this.columnName + "]"; + } + } } diff --git a/spring-test-dbunit/src/main/java/com/github/springtestdbunit/annotation/ExpectedDatabase.java b/spring-test-dbunit/src/main/java/com/github/springtestdbunit/annotation/ExpectedDatabase.java index 2798c417..0e876235 100644 --- a/spring-test-dbunit/src/main/java/com/github/springtestdbunit/annotation/ExpectedDatabase.java +++ b/spring-test-dbunit/src/main/java/com/github/springtestdbunit/annotation/ExpectedDatabase.java @@ -51,4 +51,27 @@ * @return Database assertion mode to use. */ DatabaseAssertionMode assertionMode() default DatabaseAssertionMode.DEFAULT; + + /** + * Flag used to indicate that we want to change the sequence names in an XML file for their values in the database. + *

+ * + * This allows us to use, for example, an xml file for the dataset that looks like this :
+ * + * + * + * + * + * + * + * + * + * The {@link org.dbunit.dataset.IDataSet} however will contain the real ids. Each sequence in the database is only accessed once (to + * know its current value), and the rest of the values are calculated based on that value and the number of times + * the sequence name is declared in the xml file. + * + * @return true if we want to change the sequence names for their values in the database, + * false otherwise + */ + boolean replaceSequenceIds() default false; }