Skip to content
Open
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
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ dependencies {
exclude(group = "org.apache.logging.log4j", module = "log4j-core")
}
integrationTestRuntimeOnly("org.junit.platform:junit-platform-launcher")
integrationTestRuntimeOnly(libs.postgresql)

api(libs.jspecify)

Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ slf4j-api = "2.0.16"
logback-classic = "1.5.16"
mockito = "5.16.0"
checker-qual = "3.49.1"
postgresql = "42.7.8"

plugin-spotless = "7.0.2"
plugin-errorprone = "4.1.0"
Expand All @@ -47,6 +48,7 @@ sl4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-api" }
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-classic" }
mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
checker-qual = { module = "org.checkerframework:checker-qual", version.ref = "checker-qual" }
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgresql" }

[bundles]
test-common = ["junit-jupiter", "assertj", "logback-classic"]
Expand Down
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test classes should have the "Tests" suffix.

Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@

/** @see MongoDialect#buildSQLExceptionConversionDelegate() */
@SessionFactory(exportSchema = false)
@DomainModel(annotatedClasses = {ExceptionHandlingIntegrationTest.Item.class})
@DomainModel(annotatedClasses = {ExceptionHandlingIntegrationTests.Item.class})
@ExtendWith(MongoExtension.class)
class ExceptionHandlingIntegrationTest implements SessionFactoryScopeAware {
class ExceptionHandlingIntegrationTests implements SessionFactoryScopeAware {
private static final String COLLECTION_NAME = "items";
private static final String EXCEPTION_MESSAGE_FAILED_TO_EXECUTE_OPERATION = "Failed to execute operation";
private static final String EXCEPTION_MESSAGE_TIMEOUT = "Timeout while waiting for operation to complete";
Expand Down Expand Up @@ -101,7 +101,7 @@ void testTimeoutExceptionThrown() {
@ServiceRegistry(settings = @Setting(name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "2"))
@ExtendWith(MongoExtension.class)
@SessionFactory(exportSchema = false)
@DomainModel(annotatedClasses = {ExceptionHandlingIntegrationTest.Item.class})
@DomainModel(annotatedClasses = {ExceptionHandlingIntegrationTests.Item.class})
class Batch implements SessionFactoryScopeAware {
SessionFactoryScope sessionFactoryScope;

Expand Down
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was renamed to NativeBootstrappingIntegrationTests. The code was also simplified, taking into account that MongoExtension now disables fail points.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.mongodb.hibernate.boot;

import static com.mongodb.hibernate.BasicCrudIntegrationTests.Item.COLLECTION_NAME;
import static org.assertj.core.api.Assertions.assertThat;

import com.mongodb.client.MongoCollection;
Expand All @@ -41,18 +42,12 @@
annotatedClasses = JakartaPersistenceBootstrappingIntegrationTests.Item.class)
@ExtendWith(MongoExtension.class)
class JakartaPersistenceBootstrappingIntegrationTests {
private static final String COLLECTION_NAME = "items";

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

@Test
void smoke(EntityManagerFactoryScope scope) {
scope.inTransaction(em -> {
var item = new Item();
item.id = 1;
em.persist(item);
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only drive-by changes here to make the test code simpler.

scope.inTransaction(em -> em.persist(new Item(1)));
assertThat(mongoCollection.find()).containsExactly(BsonDocument.parse("{_id: 1}"));
}

Expand All @@ -61,5 +56,9 @@ void smoke(EntityManagerFactoryScope scope) {
static class Item {
@Id
int id;

Item(int id) {
this.id = id;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2024-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.boot;

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

import com.mongodb.client.MongoClient;
import com.mongodb.hibernate.junit.InjectMongoClient;
import com.mongodb.hibernate.junit.MongoExtension;
import org.bson.BsonDocument;
import org.hibernate.boot.MetadataSources;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MongoExtension.class)
class NativeBootstrappingIntegrationTests {
@InjectMongoClient
private static MongoClient mongoClient;

@Test
@SuppressWarnings("try")
void testCouldNotInstantiateDialectExceptionMessage() {
assertThatThrownBy(() -> {
BsonDocument failPointCommand = BsonDocument.parse(
"""
{
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": ["buildInfo"],
"errorCode": 1
}
}
""");
mongoClient.getDatabase("admin").runCommand(failPointCommand);
new MetadataSources().buildMetadata();
})
.hasRootCauseMessage(
"Could not instantiate [com.mongodb.hibernate.dialect.MongoDialect], see the earlier exceptions to find out why");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.boot;

import static com.mongodb.hibernate.BasicCrudIntegrationTests.Item.COLLECTION_NAME;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import com.mongodb.hibernate.internal.boot.MongoAdditionalMappingContributor;
import com.mongodb.hibernate.junit.MongoExtension;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.InstantiationException;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@Jpa(
exportSchema = false,
integrationSettings = {
@Setting(name = AvailableSettings.DIALECT, value = "org.hibernate.dialect.PostgreSQLDialect"),
@Setting(name = AvailableSettings.JAKARTA_JDBC_URL, value = "postgresql://"),
@Setting(name = AvailableSettings.JAKARTA_JDBC_DRIVER, value = "org.postgresql.Driver"),
@Setting(name = DriverManagerConnectionProviderImpl.INITIAL_SIZE, value = "0"),
},
annotatedClasses = PostgreSQLBootstrappingIntegrationTests.Item.class)
@ExtendWith(MongoExtension.class)
class PostgreSQLBootstrappingIntegrationTests {
/**
* Verify that {@link MongoAdditionalMappingContributor} skips its logic when bootstrapping is unrelated to the
* MongoDB Extension for Hibernate ORM.
*/
@Test
void testMongoAdditionalMappingContributorIsSkipped(EntityManagerFactoryScope scope) {
assertThatThrownBy(() -> scope.inTransaction(em -> em.persist(new Item(null))))
.hasMessageNotContaining("does not support primary key spanning multiple columns")
.isInstanceOf(InstantiationException.class)
.hasMessageMatching("Could not instantiate entity .* due to: null");
}

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

Item(MultipleColumns id) {
this.id = id;
}
}

@Embeddable
record MultipleColumns(int a, int b) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.mongodb.hibernate.internal.extension;
package com.mongodb.hibernate.internal.boot;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Most of the program elements we have represent part of the extension. The extension in the package name does not make sense, though originally I thought it would make sense.
  2. org.hibernate.boot.spi.AdditionalMappingContributor is in the boot package, which is why I put MongoAdditionalMappingContributor to the boot package.


import static com.mongodb.hibernate.internal.MongoAssertions.assertFalse;
import static com.mongodb.hibernate.internal.MongoAssertions.assertInstanceOf;
Expand All @@ -23,6 +23,7 @@
import static com.mongodb.hibernate.internal.MongoConstants.MONGO_DBMS_NAME;
import static java.lang.String.format;

import com.mongodb.hibernate.dialect.MongoDialect;
import com.mongodb.hibernate.internal.FeatureNotSupportedException;
import jakarta.persistence.Embeddable;
import java.sql.Time;
Expand Down Expand Up @@ -83,6 +84,10 @@ public void contribute(
InFlightMetadataCollector metadata,
ResourceStreamLocator resourceStreamLocator,
MetadataBuildingContext buildingContext) {
if (!(metadata.getDatabase().getDialect() instanceof MongoDialect)) {
// avoid interfering with bootstrapping unrelated to the MongoDB Extension for Hibernate ORM
return;
}
Comment on lines +81 to +90
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix.

forbidEmbeddablesWithoutPersistentAttributes(metadata);
metadata.getEntityBindings().forEach(persistentClass -> {
checkPropertyTypes(persistentClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@

/** The program elements within this package are not part of the public API and may be removed or changed at any time */
@NullMarked
package com.mongodb.hibernate.internal.extension;
package com.mongodb.hibernate.internal.boot;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.mongodb.hibernate.internal.extension.service;
package com.mongodb.hibernate.internal.service;

import static com.mongodb.hibernate.internal.VisibleForTesting.AccessModifier.PRIVATE;
import static java.lang.String.format;
Expand Down Expand Up @@ -84,8 +84,7 @@ public StandardServiceRegistryScopedState initiateService(
private MongoConfiguration createMongoConfiguration(
Map<String, Object> configurationValues, ServiceRegistryImplementor serviceRegistry) {
var jdbcUrl = configurationValues.get(JAKARTA_JDBC_URL);
MongoConfigurationContributor mongoConfigurationContributor =
getMongoConfigurationContributor(serviceRegistry);
var mongoConfigurationContributor = getMongoConfigurationContributor(serviceRegistry);
if (jdbcUrl == null && mongoConfigurationContributor == null) {
throw new HibernateException(format(
"Configuration property [%s] is required unless %s is provided",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@

/** The program elements within this package are not part of the public API and may be removed or changed at any time */
@NullMarked
package com.mongodb.hibernate.internal.extension.service;
package com.mongodb.hibernate.internal.service;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import static org.hibernate.query.sqm.FetchClauseType.ROWS_ONLY;

import com.mongodb.hibernate.internal.FeatureNotSupportedException;
import com.mongodb.hibernate.internal.extension.service.StandardServiceRegistryScopedState;
import com.mongodb.hibernate.internal.service.StandardServiceRegistryScopedState;
import com.mongodb.hibernate.internal.translate.mongoast.AstDocument;
import com.mongodb.hibernate.internal.translate.mongoast.AstElement;
import com.mongodb.hibernate.internal.translate.mongoast.AstFieldUpdate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import com.mongodb.client.MongoClients;
import com.mongodb.hibernate.internal.BuildConfig;
import com.mongodb.hibernate.internal.VisibleForTesting;
import com.mongodb.hibernate.internal.extension.service.StandardServiceRegistryScopedState;
import com.mongodb.hibernate.internal.service.StandardServiceRegistryScopedState;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectOutputStream;
Expand Down Expand Up @@ -58,6 +58,8 @@ public final class MongoConnectionProvider implements ConnectionProvider, Stoppa
private @Nullable StandardServiceRegistryScopedState standardServiceRegistryScopedState;
private transient @Nullable MongoClient mongoClient;

public MongoConnectionProvider() {}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the constructor used by Hibernate ORM explicit, like we do in other places.


@Override
public Connection getConnection() throws SQLException {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
com.mongodb.hibernate.internal.extension.MongoAdditionalMappingContributor
# Hibernate ORM runs additional mapping contributors for any
# `org.hibernate.SessionFactory`/`jakarta.persistence.EntityManagerFactory` that is being bootstrapped.
# Consequently, this contributor must check that the involved dialect is `com.mongodb.hibernate.dialect.MongoDialect`,
# to avoid interfering with bootstrapping unrelated to the MongoDB Extension for Hibernate ORM.
com.mongodb.hibernate.internal.boot.MongoAdditionalMappingContributor
Loading