Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4b65eb2
Introduce batch support.
vbabanin Sep 11, 2025
5b17d58
Add tests and refactor MongoStatements.
vbabanin Oct 2, 2025
3f1ebcb
Merge branch 'main' into HIBERNATE-40
vbabanin Oct 2, 2025
ded2bb7
Rename tests.
vbabanin Oct 2, 2025
92d9c41
Rename variables.
vbabanin Oct 2, 2025
b78cb77
Rename methods, add tests.
vbabanin Oct 2, 2025
a7f7792
Revert to private.
vbabanin Oct 2, 2025
7372d56
Add JDBC exception handling.
vbabanin Oct 7, 2025
330de0f
Apply spotless.
vbabanin Oct 7, 2025
5c81496
Apply spotless.
vbabanin Oct 7, 2025
e4d7afd
Revert SqlTransientException handling.
vbabanin Oct 8, 2025
183aafc
Remove SQlConsumer.
vbabanin Oct 8, 2025
9128c9e
Convert SQLException's to Hibernate ORM exception's.
vbabanin Oct 8, 2025
35dbc91
Remove public modifier.
vbabanin Oct 9, 2025
6fe48b4
Remove disabled tests.
vbabanin Oct 17, 2025
9577603
Fix issues.
vbabanin Oct 21, 2025
76acbc5
Update src/integrationTest/java/com/mongodb/hibernate/jdbc/MongoPrepa…
vbabanin Oct 25, 2025
39df511
Update src/integrationTest/java/com/mongodb/hibernate/jdbc/MongoPrepa…
vbabanin Oct 25, 2025
11a2b07
Update src/integrationTest/java/com/mongodb/hibernate/jdbc/MongoPrepa…
vbabanin Oct 25, 2025
510bc2d
Update src/integrationTest/java/com/mongodb/hibernate/jdbc/MongoPrepa…
vbabanin Oct 27, 2025
6faa9cb
Fix batch update count reporting.
vbabanin Oct 28, 2025
b27ef0e
Merge branch 'main' into HIBERNATE-40
vbabanin Oct 28, 2025
7c8a702
Make NULL_SQL_STATE private.
vbabanin Oct 28, 2025
51103be
Remove final in parameters.
vbabanin Oct 28, 2025
06b1ee0
Merge branch 'HIBERNATE-40' into HIBERNATE-95
vbabanin Oct 28, 2025
bbade89
Move failpoint to beforeEach and afterEach.
vbabanin Oct 28, 2025
0cda4bc
Add afterAll.
vbabanin Oct 28, 2025
ad94e8f
Update src/integrationTest/java/com/mongodb/hibernate/junit/MongoExte…
vbabanin Oct 29, 2025
17af7e8
Use JdbcException for all cases.
vbabanin Oct 29, 2025
c7a67dc
Update src/integrationTest/java/com/mongodb/hibernate/exception/Excep…
vbabanin Oct 30, 2025
9d0b258
Move tests to .*hibernate root package.
vbabanin Oct 30, 2025
2057907
Merge branch 'main' into HIBERNATE-95
vbabanin Oct 30, 2025
1a22030
Spotless apply.
vbabanin Oct 30, 2025
8414f45
Merge branch 'main' into HIBERNATE-95
vbabanin Oct 30, 2025
95d3448
Spotless apply.
vbabanin Oct 30, 2025
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
@@ -0,0 +1,63 @@
/*
* Copyright 2025-present MongoDB, Inc.
*
* 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 com.mongodb.hibernate.exception;

import com.mongodb.client.MongoClient;
import com.mongodb.hibernate.junit.InjectMongoClient;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
import org.junit.jupiter.api.AfterEach;

class AbstractExceptionHandlingIntegrationTest implements SessionFactoryScopeAware {
private static final String FAIL_COMMAND_NAME = "failCommand";

@InjectMongoClient
private static MongoClient mongoClient;

SessionFactoryScope sessionFactoryScope;

@Override
public void injectSessionFactoryScope(SessionFactoryScope sessionFactoryScope) {
this.sessionFactoryScope = sessionFactoryScope;
}

static void configureFailPointErrorCode(int errorCode) {
BsonDocument failPointCommand = BsonDocument.parse("{"
+ " configureFailPoint: \"failCommand\","
+ " mode: { times: 1 },"
+ " data: {"
+ " failCommands: [\"insert\"],"
+ " errorCode: " + errorCode
+ " errorLabels: [\"TransientTransactionError\"]"
+ " }"
+ "}");
mongoClient.getDatabase("admin").runCommand(failPointCommand);
}

private static void disableFailPoint(final String failPoint) {
BsonDocument failPointDocument =
new BsonDocument("configureFailPoint", new BsonString(failPoint)).append("mode", new BsonString("off"));
mongoClient.getDatabase("admin").runCommand(failPointDocument);
}

@AfterEach
void tearDown() {
disableFailPoint(FAIL_COMMAND_NAME);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Copyright 2025-present MongoDB, Inc.
*
* 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 com.mongodb.hibernate.exception;

import static org.assertj.core.api.Assertions.assertThatThrownBy;

import com.mongodb.MongoException;
import com.mongodb.client.MongoCollection;
import com.mongodb.hibernate.junit.InjectMongoCollection;
import com.mongodb.hibernate.junit.MongoExtension;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.sql.BatchUpdateException;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLTimeoutException;
import org.bson.BsonDocument;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.Setting;
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)
@SessionFactory(exportSchema = false)
@DomainModel(annotatedClasses = {ExceptionHandlingIntegrationTest.Item.class})
class ExceptionHandlingIntegrationTest extends AbstractExceptionHandlingIntegrationTest {
private static final String COLLECTION_NAME = "items";

@InjectMongoCollection(COLLECTION_NAME)
private static MongoCollection<BsonDocument> mongoCollection;

@Test
void testConstraintViolationExceptionThrown() {
mongoCollection.insertOne(BsonDocument.parse("{_id: 1}"));
assertThatThrownBy(() -> {
sessionFactoryScope.inTransaction(session -> {
session.persist(new Item(1));
});
})
.isInstanceOf(ConstraintViolationException.class)
.hasMessageContaining("Failed to execute operation")
.cause()
.isInstanceOf(SQLIntegrityConstraintViolationException.class)
.hasRootCauseInstanceOf(MongoException.class);
}

@ParameterizedTest
@ValueSource(ints = {1000, 1200})
void testGenericExceptionThrown(int errorCode) {
configureFailPointErrorCode(errorCode);
assertThatThrownBy(() -> {
sessionFactoryScope.inTransaction(session -> {
session.persist(new Item(1));
});
})
.isInstanceOf(GenericJDBCException.class)
.hasMessageContaining("Failed to execute operation")
.cause()
.isInstanceOf(SQLException.class)
.hasRootCauseInstanceOf(MongoException.class);
}

@Test
void testTimeoutExceptionThrown() {
configureFailPointErrorCode(50);
assertThatThrownBy(() -> {
sessionFactoryScope.inTransaction(session -> {
session.persist(new Item(1));
});
})
.isInstanceOf(GenericJDBCException.class)
.hasMessageContaining("Timeout while waiting for operation to complete")
.cause()
.isInstanceOf(SQLTimeoutException.class)
.hasRootCauseInstanceOf(MongoException.class);
}

@Nested
@ServiceRegistry(settings = @Setting(name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "2"))
@ExtendWith(MongoExtension.class)
@SessionFactory(exportSchema = false)
@DomainModel(annotatedClasses = {ExceptionHandlingIntegrationTest.Item.class})
class Batch extends AbstractExceptionHandlingIntegrationTest {

private static final String EXCEPTION_MESSAGE = "Batch execution failed";

@Test
void testConstraintViolationExceptionThrown() {
mongoCollection.insertOne(BsonDocument.parse("{_id: 1}"));
assertThatThrownBy(() -> {
sessionFactoryScope.inTransaction(session -> {
session.persist(new Item(1));
session.persist(new Item(2));
});
})
.isInstanceOf(ConstraintViolationException.class)
.hasMessageContaining(EXCEPTION_MESSAGE)
.cause()
.isInstanceOf(BatchUpdateException.class)
.cause()
.isInstanceOf(SQLIntegrityConstraintViolationException.class)
.hasRootCauseInstanceOf(MongoException.class);
}

@ParameterizedTest
@ValueSource(ints = {1000, 1200})
void testGenericExceptionThrown(int errorCode) {
configureFailPointErrorCode(errorCode);
assertThatThrownBy(() -> {
sessionFactoryScope.inTransaction(session -> {
session.persist(new Item(1));
session.persist(new Item(2));
});
})
.isInstanceOf(GenericJDBCException.class)
.hasMessageContaining(EXCEPTION_MESSAGE)
.cause()
.isInstanceOf(BatchUpdateException.class)
.hasRootCauseInstanceOf(MongoException.class);
}

@Test
void testTimeoutExceptionThrown() {
configureFailPointErrorCode(50);
assertThatThrownBy(() -> {
sessionFactoryScope.inTransaction(session -> {
session.persist(new Item(1));
session.persist(new Item(2));
});
})
.isInstanceOf(GenericJDBCException.class)
.hasMessageContaining("Batch execution failed]")
.cause()
.isInstanceOf(BatchUpdateException.class)
.cause()
.isInstanceOf(SQLTimeoutException.class)
.hasRootCauseInstanceOf(MongoException.class);
}
}

@Entity
@Table(name = COLLECTION_NAME)
static class Item {
@Id
int id;

Item() {}

Item(int id) {
this.id = id;
}
}
}
Loading