Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</properties>

<body>
<release version="3.4.1-SNAPSHOT" date="TBD" description="A documentation-site overhaul (new tutorials, 9 database vendor guides, a class-by-class Core Components reference, consolidated Filters/Datasets/Operations pages, and a new Developing DbUnit contributor section covering coding standards, commit/changelog requirements, and the GitHub workflow); an opt-in all-column sort for tables without a primary key; an opt-in sort-on-filtered-columns-only mode for DefaultPrepAndExpectedTestCase fixing false failures on tables with a generated/identity first column; MultiSchemaMySqlMetadataHandler for multi-schema MySQL connections; and multiple bug fixes including escape-pattern handling, primary-key filter fallback, empty-dataset DTD export, DatabaseDataSet initialization order, and a from-scratch clean-build pass across Javadoc doclint, Checkstyle, and compiler warnings">
<release version="3.4.1-SNAPSHOT" date="TBD" description="A documentation-site overhaul (new tutorials, 9 database vendor guides, a class-by-class Core Components reference, consolidated Filters/Datasets/Operations pages, and a new Developing DbUnit contributor section covering coding standards, commit/changelog requirements, and the GitHub workflow); an opt-in all-column sort for tables without a primary key; an opt-in sort-on-filtered-columns-only mode for DefaultPrepAndExpectedTestCase fixing false failures on tables with a generated/identity first column; MultiSchemaMySqlMetadataHandler for multi-schema MySQL connections; constructor-injected per-connection DatabaseConfig support for DataSourceDatabaseTester; and multiple bug fixes including escape-pattern handling, primary-key filter fallback, empty-dataset DTD export, DatabaseDataSet initialization order, and a from-scratch clean-build pass across Javadoc doclint, Checkstyle, and compiler warnings">
<action dev="jeffjensen" type="add" issue="840" system="github" due-to="jeffjensen">
Add repo-root README.adoc, rendered natively by GitHub via Asciidoctor, so the repository landing page shows a pitch, build/reproducible-build badges, a pointer to the "dbUnit in 5 Minutes" tutorial, and links to the documentation site, Maven coordinates, GitHub Discussions, and CONTRIBUTING.md instead of nothing.
</action>
Expand Down Expand Up @@ -224,6 +224,9 @@
<action dev="jeffjensen" type="fix" issue="708" system="github" due-to="roel-tjin">
Fix NoSuchColumnException when two same-named tables merged by CompositeDataSet (e.g. from two separate flat-XML datasets both inserting into the same table) disagree on columns. A row belonging to a part that never declared an optional column crashed instead of being treated as not supplied, hitting InsertOperation's core insert path via equalsIgnoreMapping/getIgnoreMapping. InsertOperation.getIgnoreMapping/equalsIgnoreMapping now resolve such a column to ITable.NO_VALUE, the same sentinel already used to omit a column from a generated statement, instead of letting the row's NoSuchColumnException propagate. This is deliberately scoped to InsertOperation alone rather than CompositeTable itself: UpdateOperation and DeleteOperation bind every requested column's value directly with no equivalent ignore-mapping, so a genuinely missing column there must keep throwing instead of silently binding NULL into a WHERE clause.
</action>
<action dev="jeffjensen" type="add" issue="707" system="github" due-to="pmoukhataev">
Add a DataSourceDatabaseTester(DataSource, String schema, CachingConnectionProvider, DatabaseConfig) constructor and a new DatabaseConfig#copyPropertiesInto(DatabaseConfig) method, so per-connection properties and features (e.g. PROPERTY_DATATYPE_FACTORY for a specific database) can be applied to every connection a DataSourceDatabaseTester creates directly through the constructor, instead of requiring an IOperationListener#connectionRetrieved() override to reach into each connection's DatabaseConfig after the fact. Also fix DatabaseConfig's own constructor, which left FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES's underlying property value unset despite ALL_PROPERTIES declaring it non-nullable - harmless until copyPropertiesInto()'s full iteration over ALL_PROPERTIES became the first caller to round-trip every declared property through setProperty(), which enforces that constraint strictly.
</action>
</release>
<release version="3.4.0" date="Jul 28, 2026" description="Test-suite hardening (un-skip and strengthen dozens of disabled/no-op tests); add CachingConnectionProvider and reduce DefaultPrepAndExpectedTestCase's per-test connection churn; pin identifier case-folding to Locale.ENGLISH for Turkish-locale correctness; and a broad set of correctness fixes across export formats (XML, YAML, CSV, XLS, Ant), TimestampDataType timezone handling, InsertOperation/TransactionOperation, and resource-leak cleanups">
<action dev="jeffjensen" type="fix" issue="797" system="github" due-to="jeffjensen">
Expand Down
32 changes: 31 additions & 1 deletion src/main/java/org/dbunit/DataSourceDatabaseTester.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.sql.DataSource;

import org.dbunit.database.CachingConnectionProvider;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;

Expand All @@ -47,6 +48,7 @@ public class DataSourceDatabaseTester extends AbstractDatabaseTester
private static final Logger logger = LoggerFactory.getLogger(DataSourceDatabaseTester.class);

private final CachingConnectionProvider connectionProvider;
private final DatabaseConfig databaseConfig;
private DataSource dataSource;

/**
Expand Down Expand Up @@ -88,6 +90,28 @@ public DataSourceDatabaseTester(DataSource dataSource, String schema)
*/
public DataSourceDatabaseTester(DataSource dataSource, String schema,
CachingConnectionProvider connectionProvider)
{
this(dataSource, schema, connectionProvider, null);
}

/**
* Creates a new DataSourceDatabaseTester with the specified DataSource, schema name,
* optional {@link CachingConnectionProvider}, and optional {@link DatabaseConfig} whose
* property and feature values are applied to every connection this tester creates -
* for example to set {@link DatabaseConfig#PROPERTY_DATATYPE_FACTORY} for a specific
* database without needing an {@link IOperationListener}.
*
* @param dataSource The DataSource to pull connections from.
* @param schema The schema name to be used for new dbunit connections - can be <code>null</code>.
* @param connectionProvider Caches and validates the connection across calls - can be
* <code>null</code>, in which case a new connection is created on every call as before.
* @param databaseConfig The property and feature values to apply to every connection this
* tester creates - can be <code>null</code>, in which case each connection keeps its
* own default {@link DatabaseConfig}.
* @since 3.4.1
*/
public DataSourceDatabaseTester(DataSource dataSource, String schema,
CachingConnectionProvider connectionProvider, DatabaseConfig databaseConfig)
{
super(schema);

Expand All @@ -97,6 +121,7 @@ public DataSourceDatabaseTester(DataSource dataSource, String schema,
}
this.dataSource = dataSource;
this.connectionProvider = connectionProvider;
this.databaseConfig = databaseConfig;
}

public IDatabaseConnection getConnection() throws Exception
Expand All @@ -113,6 +138,11 @@ public IDatabaseConnection getConnection() throws Exception

private IDatabaseConnection createConnection() throws Exception
{
return new DatabaseConnection( dataSource.getConnection(), getSchema() );
IDatabaseConnection connection = new DatabaseConnection( dataSource.getConnection(), getSchema() );
if (databaseConfig != null)
{
databaseConfig.copyPropertiesInto(connection.getConfig());
}
return connection;
}
}
20 changes: 19 additions & 1 deletion src/main/java/org/dbunit/database/DatabaseConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ public DatabaseConfig()
setFeature(FEATURE_QUALIFIED_TABLE_NAMES, false);
setFeature(FEATURE_CASE_SENSITIVE_TABLE_NAMES, false);
setFeature(FEATURE_DATATYPE_WARNING, true);
setFeature(FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES, false);
setFeature(FEATURE_ALLOW_EMPTY_FIELDS, false);
setFeature(FEATURE_SORT_ALL_COLUMNS_WHEN_NO_PRIMARY_KEY, false);

Expand Down Expand Up @@ -280,7 +281,24 @@ public Object getProperty(String name)
return _propertyMap.get(name);
}

private Object convertIfNeeded(String property, Object value)
/**
* Copies every known property and feature value from this config into the given target config.
*
* @param target The config to receive this config's property and feature values.
* @since 3.4.1
*/
public void copyPropertiesInto(DatabaseConfig target)
{
logger.trace("copyPropertiesInto(target={}) - start", target);

for (ConfigProperty configProperty : ALL_PROPERTIES)
{
String property = configProperty.getProperty();
target.setProperty(property, getProperty(property));
}
}

private Object convertIfNeeded(String property, Object value)
{
logger.trace("convertIfNeeded(property={}, value={}) - start", property, value);

Expand Down
48 changes: 48 additions & 0 deletions src/test/java/org/dbunit/DataSourceDatabaseTesterIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import javax.sql.DataSource;

import org.dbunit.database.CachingConnectionProvider;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.IDatabaseConnection;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -118,6 +119,53 @@ void testConstructor_withNullDataSourceAndProvider_throwsNullPointerException()
.isInstanceOf(NullPointerException.class);
}

@Test
void testGetConnection_withDatabaseConfig_appliesConfiguredPropertiesToConnection()
throws Exception
{
final DatabaseConfig databaseConfig = new DatabaseConfig();
databaseConfig.setProperty(DatabaseConfig.PROPERTY_BATCH_SIZE, 500);
final DataSourceDatabaseTester tester =
new DataSourceDatabaseTester(newDataSource(), null, null, databaseConfig);

final IDatabaseConnection connection = tester.getConnection();

openedConnection = connection;
assertThat(connection.getConfig().getProperty(DatabaseConfig.PROPERTY_BATCH_SIZE))
.as("The DatabaseConfig supplied to the constructor must be applied to every "
+ "connection this tester creates.")
.isEqualTo(500);
}

@Test
void testGetConnection_withDatabaseConfigAndProvider_appliesConfiguredPropertiesToCachedConnection()
throws Exception
{
final DatabaseConfig databaseConfig = new DatabaseConfig();
databaseConfig.setProperty(DatabaseConfig.PROPERTY_BATCH_SIZE, 500);
final CachingConnectionProvider provider = new CachingConnectionProvider();
final DataSourceDatabaseTester tester =
new DataSourceDatabaseTester(newDataSource(), null, provider, databaseConfig);

final IDatabaseConnection connection = tester.getConnection();

openedConnection = connection;
assertThat(connection.getConfig().getProperty(DatabaseConfig.PROPERTY_BATCH_SIZE))
.as("The DatabaseConfig supplied to the constructor must be applied even when a "
+ "CachingConnectionProvider is used.")
.isEqualTo(500);
}

@Test
void testConstructor_withNullDataSourceAndProviderAndConfig_throwsNullPointerException()
{
assertThatThrownBy(() -> new DataSourceDatabaseTester(null, null,
new CachingConnectionProvider(), new DatabaseConfig()))
.as("The 4-arg constructor must reject a null DataSource just like the "
+ "pre-existing constructors do.")
.isInstanceOf(NullPointerException.class);
}

private static DataSource newDataSource()
{
final JdbcDataSource dataSource = new JdbcDataSource();
Expand Down
35 changes: 35 additions & 0 deletions src/test/java/org/dbunit/database/DatabaseConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,39 @@ void testSetFeatureViaSetFeatureMethod_withBooleanTrue_setsFeatureToTrue() throw
.isTrue();
}

@Test
void testCopyPropertiesInto_withConfiguredPropertyAndFeature_copiesValuesToTargetConfig()
throws Exception
{
final DatabaseConfig source = new DatabaseConfig();
source.setProperty(DatabaseConfig.PROPERTY_BATCH_SIZE, 500);
source.setFeature(DatabaseConfig.FEATURE_BATCHED_STATEMENTS, true);
final DatabaseConfig target = new DatabaseConfig();

source.copyPropertiesInto(target);

assertThat(target.getProperty(DatabaseConfig.PROPERTY_BATCH_SIZE))
.as("copyPropertiesInto() must copy a configured property value to the target config.")
.isEqualTo(500);
assertThat(target.getFeature(DatabaseConfig.FEATURE_BATCHED_STATEMENTS))
.as("copyPropertiesInto() must copy a configured feature value to the target config.")
.isTrue();
}

@Test
void testCopyPropertiesInto_withNullablePropertyAtDefault_overwritesTargetWithNull()
throws Exception
{
final DatabaseConfig source = new DatabaseConfig();
final DatabaseConfig target = new DatabaseConfig();
target.setProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN, "[?]");

source.copyPropertiesInto(target);

assertThat(target.getProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN))
.as("copyPropertiesInto() must overwrite the target's property even when the "
+ "source left it at its nullable default.")
.isNull();
}

}
Loading