Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
import static com.mongodb.hibernate.jdbc.MongoStatementIntegrationTests.doAndTerminateTransaction;
import static com.mongodb.hibernate.jdbc.MongoStatementIntegrationTests.doWithSpecifiedAutoCommit;
import static com.mongodb.hibernate.jdbc.MongoStatementIntegrationTests.insertTestData;
import static java.lang.String.format;
import static java.sql.Statement.SUCCESS_NO_INFO;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.mongodb.client.MongoCollection;
Expand All @@ -35,7 +38,10 @@
import com.mongodb.hibernate.junit.MongoExtension;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
Expand All @@ -47,9 +53,12 @@
import org.junit.jupiter.api.AutoClose;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

@ExtendWith(MongoExtension.class)
class MongoPreparedStatementIntegrationTests {
Expand Down Expand Up @@ -124,8 +133,19 @@ void testExecuteQuery() {
}

@Test
void testPreparedStatementAndResultSetRoundTrip() {
void testPreparedStatementExecuteUpdateAndResultSetRoundTrip() {
assertRoundTrip(PreparedStatement::executeUpdate);
}

@Test
void testPreparedStatementExecuteBatchAndResultSetRoundTrip() {
assertRoundTrip(preparedStatement -> {
preparedStatement.addBatch();
preparedStatement.executeBatch();
});
}

private void assertRoundTrip(SqlConsumer<PreparedStatement> executor) {
var random = new Random();

boolean booleanValue = random.nextBoolean();
Expand Down Expand Up @@ -165,8 +185,7 @@ void testPreparedStatementAndResultSetRoundTrip() {
pstmt.setString(5, stringValue);
pstmt.setBigDecimal(6, bigDecimalValue);
pstmt.setBytes(7, bytes);

pstmt.executeUpdate();
executor.accept(pstmt);
}
});

Expand Down Expand Up @@ -210,6 +229,241 @@ void testPreparedStatementAndResultSetRoundTrip() {
});
}

@Nested
class ExecuteBatchTests {
private static final String INSERT_MQL =
"""
{
insert: "books",
documents: [
{
_id: 1,
title: "War and Peace"
},
{
_id: 2,
title: "Anna Karenina"
},
{
_id: 3,
title: "Crime and Punishment"
}
]
}""";

@Test
void testEmptyBatch() {
doWorkAwareOfAutoCommit(connection -> {
try {
var pstmt = (MongoPreparedStatement)
connection.prepareStatement(
"""
{
insert: "books",
documents: [
{
_id: 1
}
]
}""");
int[] updateCounts = pstmt.executeBatch();
assertEquals(0, updateCounts.length);
} catch (SQLException e) {
throw new RuntimeException(e);
}
});

assertThat(mongoCollection.find()).isEmpty();
}

@Test
@DisplayName("Test statement’s batch queue is reset once executeBatch returns")
void testBatchQueueIsResetAfterExecute() {
doWorkAwareOfAutoCommit(connection -> {
var pstmt = (MongoPreparedStatement)
connection.prepareStatement(
"""
{
insert: "books",
documents: [
{
_id: {$undefined: true},
title: {$undefined: true}
}
]
}""");

pstmt.setInt(1, 1);
pstmt.setString(2, "War and Peace");
pstmt.addBatch();
assertExecuteBatch(pstmt, 1);

assertExecuteBatch(pstmt, 0);
});

assertThat(mongoCollection.find())
.containsExactly(
BsonDocument.parse(
"""
{
_id: 1,
title: "War and Peace"
}"""));
}

@Test
@DisplayName("Test values set for the parameter markers of PreparedStatement are not reset when it is executed")
void testBatchParametersReuse() {
doWorkAwareOfAutoCommit(connection -> {
var pstmt = (MongoPreparedStatement)
connection.prepareStatement(
"""
{
insert: "books",
documents: [
{
_id: {$undefined: true},
title: {$undefined: true}
}
]
}""");

pstmt.setInt(1, 1);
pstmt.setString(2, "War and Peace");
pstmt.addBatch();
assertExecuteBatch(pstmt, 1);

pstmt.setInt(1, 2);
// No need to set title again, it should be reused from the previous execution
pstmt.addBatch();
assertExecuteBatch(pstmt, 1);
});

assertThat(mongoCollection.find())
.containsExactly(
BsonDocument.parse(
"""
{
_id: 1,
title: "War and Peace"
}"""),
BsonDocument.parse(
"""
{
_id: 2,
title: "War and Peace"
}"""));
}

@Test
void testBatchInsert() {
int batchCount = 3;
doWorkAwareOfAutoCommit(connection -> {
var pstmt = (MongoPreparedStatement)
connection.prepareStatement(
"""
{
insert: "books",
documents: [{
_id: {$undefined: true},
title: {$undefined: true}
}]
}""");

for (int i = 1; i <= batchCount; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, "Book " + i);
pstmt.addBatch();
}
assertExecuteBatch(pstmt, batchCount);
});

var expectedDocs = new ArrayList<BsonDocument>();
for (int i = 0; i < batchCount; i++) {
expectedDocs.add(BsonDocument.parse(format(
"""
{
"_id": %d,
"title": "Book %d"
}""",
i + 1, i + 1)));
}
assertThat(mongoCollection.find()).containsExactlyElementsOf(expectedDocs);
}

@Test
void testBatchUpdate() {
insertTestData(session, INSERT_MQL);

int batchCount = 3;
doWorkAwareOfAutoCommit(connection -> {
var pstmt = (MongoPreparedStatement)
connection.prepareStatement(
"""
{
update: "books",
updates: [{
q: { _id: { $undefined: true } },
u: { $set: { title: { $undefined: true } } },
multi: true
}]
}""");
for (int i = 1; i <= batchCount; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, "Book " + i);
pstmt.addBatch();
}
assertExecuteBatch(pstmt, batchCount);
});

var expectedDocs = new ArrayList<BsonDocument>();
for (int i = 0; i < batchCount; i++) {
expectedDocs.add(BsonDocument.parse(format(
"""
{
"_id": %d,
"title": "Book %d"
}""",
i + 1, i + 1)));
}
assertThat(mongoCollection.find()).containsExactlyElementsOf(expectedDocs);
}

@Test
void testBatchDelete() {
insertTestData(session, INSERT_MQL);

int batchCount = 3;
doWorkAwareOfAutoCommit(connection -> {
var pstmt = (MongoPreparedStatement)
connection.prepareStatement(
"""
{
delete: "books",
deletes: [{
q: { _id: { $undefined: true } },
limit: 0
}]
}""");
for (int i = 1; i <= batchCount; i++) {
pstmt.setInt(1, i);
pstmt.addBatch();
}
assertExecuteBatch(pstmt, batchCount);
});

assertThat(mongoCollection.find()).isEmpty();
}

private void assertExecuteBatch(MongoPreparedStatement pstmt, int expectedBatchResultSize) throws SQLException {
int[] updateCounts = pstmt.executeBatch();
assertEquals(expectedBatchResultSize, updateCounts.length);
for (int updateCount : updateCounts) {
assertEquals(SUCCESS_NO_INFO, updateCount);
}
}
}

@Nested
class ExecuteUpdateTests {

Expand Down Expand Up @@ -386,6 +640,23 @@ void testDelete() {
}""")));
}

@ParameterizedTest(name = "testNotSupportedCommands. Parameters: {0}")
@ValueSource(strings = {"findAndModify", "aggregate", "bulkWrite"})
void testNotSupportedCommands(String commandName) {
doWorkAwareOfAutoCommit(connection -> {
try (PreparedStatement findAndModify = connection.prepareStatement(format(
"""
{
findAndModify: "books"
}""",
commandName))) {
SQLFeatureNotSupportedException exception =
assertThrows(SQLFeatureNotSupportedException.class, findAndModify::executeUpdate);
assertThat(exception.getMessage()).contains("findAndModify");
}
});
}

private void assertExecuteUpdate(
Function<Connection, MongoPreparedStatement> pstmtProvider,
int expectedUpdatedRowCount,
Expand All @@ -407,4 +678,8 @@ private void doWorkAwareOfAutoCommit(Work work) {
void doAwareOfAutoCommit(Connection connection, SqlExecutable work) throws SQLException {
doWithSpecifiedAutoCommit(false, connection, () -> doAndTerminateTransaction(connection, work));
}

interface SqlConsumer<T> {
void accept(T t) throws SQLException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,13 @@ protected void assertSelectQueryFailure(
expectedExceptionMessageParameters);
}

protected void assertActualCommand(BsonDocument expectedCommand) {
protected void assertActualCommand(BsonDocument... expectedCommands) {
var capturedCommands = testCommandListener.getStartedCommands();

assertThat(capturedCommands)
.singleElement()
.asInstanceOf(InstanceOfAssertFactories.MAP)
.containsAllEntriesOf(expectedCommand);
assertThat(capturedCommands).hasSize(expectedCommands.length);
for (int i = 0; i < expectedCommands.length; i++) {
BsonDocument actual = capturedCommands.get(i);
assertThat(actual).asInstanceOf(InstanceOfAssertFactories.MAP).containsAllEntriesOf(expectedCommands[i]);
}
}

protected void assertMutationQuery(
Expand Down
Loading