Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement ConnectionProvider #8

Merged
merged 42 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
da08c43
implement ConnectionProvider
NathanQingyangXu Oct 25, 2024
2c90afc
change minimal MongoDB version to 6
NathanQingyangXu Oct 28, 2024
cdba9c5
Update src/test/resources/hibernate.properties
NathanQingyangXu Nov 1, 2024
ad28ab1
remove package-info.java in test folder;
NathanQingyangXu Nov 1, 2024
bb51724
improve Javadoc for MongoConnectionProvider per review comments
NathanQingyangXu Nov 1, 2024
5fb9c5e
remove unnecessary codec setting stuff
NathanQingyangXu Nov 1, 2024
08a9603
simplify the dummy implementation of the three methods irrelevant to …
NathanQingyangXu Nov 1, 2024
77b0a72
simplify MongoConnectionProvider per code review comments
NathanQingyangXu Nov 1, 2024
46e06e4
remove exception package and move ConfigurationException to cfg package
NathanQingyangXu Nov 1, 2024
d68a0db
add missing Javadocs for MongoDialect
NathanQingyangXu Nov 1, 2024
b816075
add MQL acronym expansion in Javadoc
NathanQingyangXu Nov 1, 2024
e151069
round #2 code review comments
NathanQingyangXu Nov 4, 2024
bbd90f8
change mininum mongodb version to v6.3
NathanQingyangXu Nov 4, 2024
4036693
use HibernateException instead of ConfigurationException.java
NathanQingyangXu Nov 4, 2024
9c9622c
switch MongoDialect minimum db version to 6.0
NathanQingyangXu Nov 5, 2024
a97f2dd
Update src/main/java/com/mongodb/hibernate/jdbc/MongoConnectionProvid…
NathanQingyangXu Nov 5, 2024
4f9f958
deleted unused constructors in MongoDialect
NathanQingyangXu Nov 5, 2024
9b817a5
add Javadoc for the root package-info.java
NathanQingyangXu Nov 5, 2024
6ad89a0
make more exception messages begin with uppercase
NathanQingyangXu Nov 6, 2024
cdf49b0
update various gradle plugin to latest versions
NathanQingyangXu Nov 6, 2024
a73cf04
add sl4j logging dependencies and logback-test.xml config
NathanQingyangXu Nov 6, 2024
5c11f6c
fix a static check issue
NathanQingyangXu Nov 6, 2024
e366ea6
change default constructor of MongoDialect to end up with null value;…
NathanQingyangXu Nov 7, 2024
452c559
some minor improvements
NathanQingyangXu Nov 8, 2024
2124619
change as per Valentin's excellent code review comments
NathanQingyangXu Nov 8, 2024
196880e
update SessionFactoryTests as per code review comments
NathanQingyangXu Nov 9, 2024
8ac4c4f
update MongoDialect as per code review comments
NathanQingyangXu Nov 9, 2024
8e5eb59
update MongoConnectionProvider as per code review comments
NathanQingyangXu Nov 9, 2024
4756ea9
delete .editorconfig file and move it to another separate PR
NathanQingyangXu Nov 12, 2024
5a7ebed
fix a static check style issue
NathanQingyangXu Nov 12, 2024
51e3184
resolve some code review comments
NathanQingyangXu Nov 12, 2024
5307610
fix style checking issue
NathanQingyangXu Nov 12, 2024
7d11aa1
minor changes to improve Dialect min version usage
NathanQingyangXu Nov 13, 2024
8b3eefe
improve mentioning of MongoDB Java Driver in MongoDialect's Javadoc
NathanQingyangXu Nov 13, 2024
250673b
add transient keyword to MongoClient in MongoConnectionProvider
NathanQingyangXu Nov 13, 2024
b7c37ca
Update src/main/java/com/mongodb/hibernate/jdbc/MongoConnectionProvid…
NathanQingyangXu Nov 13, 2024
089ba52
change as per code review comments
NathanQingyangXu Nov 13, 2024
8e0d8e9
remove database checking logic
NathanQingyangXu Nov 13, 2024
1af2884
fix import style issue
NathanQingyangXu Nov 13, 2024
8dd11e2
change per some code review comments
NathanQingyangXu Nov 13, 2024
e831a88
Merge branch 'main' into HIBERNATE-27
NathanQingyangXu Nov 14, 2024
b9c98ff
add missing 'compileJava' gradle task in evergreen's static-check.sh
NathanQingyangXu Nov 14, 2024
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 @@ -14,12 +14,32 @@
* limitations under the License.
*/

package com.mongodb.hibernate.exception;
package com.mongodb.hibernate;

/**
* A temporary marker exception to denote that the feature in question is in the scope of MongoDB dialect but has not
* been implemented yet.
*
* <p>Ultimately all of its references should be eliminated sooner or later, and then this class is supposed to be
* deleted prior to product release.
*
* <p>It is recommended to provide some message to explain when it will be implemented (e.g. JIRA ticket id is a good
* idea), but that is optional.
*/
public class NotYetImplementedException extends RuntimeException {
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
stIncMale marked this conversation as resolved.
Show resolved Hide resolved

/**
* Default constructor.
*
* @deprecated use {@link NotYetImplementedException(String)} instead.
*/
public NotYetImplementedException() {}
jyemin marked this conversation as resolved.
Show resolved Hide resolved

/**
* Constructor with message parameter.
*
* @param message explanation on when the feature is to be implemented
*/
public NotYetImplementedException(String message) {
super(message);
}
Expand Down
49 changes: 0 additions & 49 deletions src/main/java/com/mongodb/hibernate/cfg/ConfigurationHelper.java

This file was deleted.

19 changes: 18 additions & 1 deletion src/main/java/com/mongodb/hibernate/dialect/MongoDialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,28 @@
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;

/**
* A MongoDB {@linkplain Dialect dialect}.
*
* <p>Usually Hibernate dialect represents some SQL RDBMS and speaks SQL with vendor-specific difference. MongoDB is a
* document DB and speaks <i>MQL</i> (MongoDB Query Language) in JSON format, but it is still possible to integrate with
NathanQingyangXu marked this conversation as resolved.
Show resolved Hide resolved
* Hibernate seamlessly by creating a JDBC adaptor on top of MongoDB's Java client library.
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>Some MongoDB-specific customization examples include:
*
* <ul>
* <li>MQL translation extension point
* <li>SQL {@linkplain java.sql.Types#ARRAY ARRAY} and {@linkplain java.sql.Types#STRUCT STRUCT} extension points to
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
* support MongoDB's embedding array and document
* <li>MQL parameterization customization
* </ul>
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
*/
public class MongoDialect extends Dialect {
jyemin marked this conversation as resolved.
Show resolved Hide resolved
NathanQingyangXu marked this conversation as resolved.
Show resolved Hide resolved
public static final int MINIMUM_MONGODB_MAJOR_VERSION_SUPPORTED = 6;
public static final int MINIMUM_MONGODB_MINOR_VERSION_SUPPORTED = 0;

private static final DatabaseVersion MINIMUM_VERSION =
DatabaseVersion.make(MINIMUM_MONGODB_MAJOR_VERSION_SUPPORTED);
DatabaseVersion.make(MINIMUM_MONGODB_MAJOR_VERSION_SUPPORTED, MINIMUM_MONGODB_MINOR_VERSION_SUPPORTED);

public MongoDialect() {
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
this(MINIMUM_VERSION);
Expand Down

This file was deleted.

101 changes: 38 additions & 63 deletions src/main/java/com/mongodb/hibernate/jdbc/MongoConnectionProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,125 +16,100 @@

package com.mongodb.hibernate.jdbc;

import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
import static org.hibernate.cfg.JdbcSettings.*;
Copy link
Member

Choose a reason for hiding this comment

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

Duplicate of #8 (comment): let's not use * imports.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

solved. Palantir won't complain so I think it is minor.

Copy link
Member

Choose a reason for hiding this comment

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

The more I learn about Palantir, the more inferior it seems to SpotBugs:

  • it's not configurable, so we can't enforce classes being final by default, for example;
  • it does not consider obviously bad practices like * imports to be bad (they may cause unnecessary name conflicts, and much worse, they may result in new name conflicts when upgrading dependencies, even when no code changes were made).

I wonder why was it chosen over SpotBugs.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't Palantir just a formatter, more akin to Checkstyle than SpotBugs?

ErrorProne is the thing that is closer to Spotbugs, and I believe it is extensible.

Copy link
Member

Choose a reason for hiding this comment

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

I am just judging based on what Nathan communicated at a catchup meeting: we can't configure the linter, at least, not nearly as flexibly as SpotBugs. If it is not Palantir but ErrorProne who does the checking, then my comment was about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't quite know much about linter so my remarks in the meeting might be wrong.
I think Palantir belongs to Spotless ecosystem. From its doc, since v7 (currently still in BETA), it does support something you desire: https://github.com/diffplug/spotless/tree/main/plugin-gradle#linting
Also, the decision to put @Nullable into the same line was from Spotless as well:
https://github.com/diffplug/spotless/tree/main/plugin-gradle#formatannotations

import static org.hibernate.internal.util.NullnessUtil.castNonNull;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.hibernate.cfg.ConfigurationHelper;
import com.mongodb.hibernate.exception.ConfigurationException;
import com.mongodb.hibernate.exception.NotYetImplementedException;
import com.mongodb.hibernate.NotYetImplementedException;
import java.sql.Connection;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.cfg.JdbcSettings;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.UnknownUnwrapTypeException;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.Startable;
import org.hibernate.service.spi.Stoppable;
import org.jspecify.annotations.Nullable;

/**
* MongoDB dialect's customized JDBC {@link ConnectionProvider} spi implementation, whose class name is supposed to be
* provided as the following Hibernate property to kick off MongoDB dialect's JDBC flow:
* MongoDB dialect's customized JDBC {@link ConnectionProvider} SPI implementation, whose class name could be provided
NathanQingyangXu marked this conversation as resolved.
Show resolved Hide resolved
* using configuration property {@value org.hibernate.cfg.JdbcSettings#CONNECTION_PROVIDER}.
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
*
* <ul>
* <li>hibernate.connection.provider_class
* </ul>
*
* <p>The following Hibernate JDBC properties will be relied upon by Hibernate's {@link Configurable} spi mechanism:
* <p>The following Hibernate JDBC properties will be relied upon by Hibernate's {@link Configurable} SPI mechanism:
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
*
* <ul>
* <li>jakarta.persistence.jdbc.url
* <li>jakarta.persistence.jdbc.user
* <li>jakarta.persistence.jdbc.password
* <li>{@linkplain org.hibernate.cfg.JdbcSettings#JAKARTA_JDBC_URL jakarta.persistence.jdbc.url}
* <li>{@linkplain org.hibernate.cfg.JdbcSettings#JAKARTA_JDBC_USER jakarta.persistence.jdbc.user}
* <li>{@linkplain org.hibernate.cfg.JdbcSettings#JAKARTA_JDBC_PASSWORD jakarta.persistence.jdbc.password}
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
* </ul>
*
* <p><code>jakarta.persistence.jdbc.url</code> property is mandatory and it maps to MongoDB's {@link ConnectionString},
* in which database name must be provided to align with JDBC URL's convention. The other two JDBC properties are
* optional.
* <p>{@value org.hibernate.cfg.JdbcSettings#JAKARTA_JDBC_URL} property is mandatory and it maps to MongoDB's <a
* href="https://www.mongodb.com/docs/manual/reference/connection-string/">connection string</a>, in which database name
* must be provided to align with JDBC URL's convention. The other two JDBC properties are optional.
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
*
* @see ConnectionProvider
* @see Configurable
* @see JdbcSettings#JAKARTA_JDBC_URL
* @see JdbcSettings#JAKARTA_JDBC_USER
* @see JdbcSettings#JAKARTA_JDBC_PASSWORD
*/
public class MongoConnectionProvider implements ConnectionProvider, Configurable, Startable, Stoppable {

// non-null after configure(Map<String, Object>) method is invoked successfully
private @Nullable ConnectionString connectionString;
private @Nullable String database;
public class MongoConnectionProvider implements ConnectionProvider, Configurable, Stoppable {
stIncMale marked this conversation as resolved.
Show resolved Hide resolved

// non-null after start() method is invoked successfully
private @Nullable MongoClient mongoClient;
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
stIncMale marked this conversation as resolved.
Show resolved Hide resolved

private @Nullable String user;
private @Nullable String password;

@Override
public Connection getConnection() {
throw new NotYetImplementedException();
throw new NotYetImplementedException("to be implemented at https://jira.mongodb.org/browse/HIBERNATE-29");
NathanQingyangXu marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public void closeConnection(Connection connection) {
throw new NotYetImplementedException();
throw new NotYetImplementedException("to be implemented at https://jira.mongodb.org/browse/HIBERNATE-29");
}

@Override
public boolean supportsAggressiveRelease() {
return false; // won't be used in container
return false;
}

@Override
public boolean isUnwrappableAs(Class<?> unwrapType) {
jyemin marked this conversation as resolved.
Show resolved Hide resolved
return ConnectionProvider.class.equals(unwrapType)
|| MongoConnectionProvider.class.isAssignableFrom(unwrapType);
return false;
}

@Override
public <T> T unwrap(Class<T> unwrapType) {
if (isUnwrappableAs(unwrapType)) {
return unwrapType.cast(this);
} else {
throw new UnknownUnwrapTypeException(unwrapType);
}
throw new UnknownUnwrapTypeException(unwrapType);
}

@Override
public void configure(Map<String, Object> configurationValues) {
var jdbcUrl = ConfigurationHelper.getRequiredConfiguration(configurationValues, JAKARTA_JDBC_URL);
public void configure(Map<String, Object> configValues) {
var jdbcUrl = configValues.get(JAKARTA_JDBC_URL);
if (jdbcUrl == null) {
throw new HibernateException("property is required: " + JAKARTA_JDBC_URL);
}
if (!(jdbcUrl instanceof String)) {
throw new HibernateException(
String.format("property ('%s') value ('%s') not of string type", JAKARTA_JDBC_URL, jdbcUrl));
}
ConnectionString connectionString;
try {
this.connectionString = new ConnectionString(jdbcUrl);
connectionString = new ConnectionString((String) jdbcUrl);
} catch (IllegalArgumentException iae) {
throw new ConfigurationException(JAKARTA_JDBC_URL, "invalid MongoDB connection string", iae);
throw new HibernateException("invalid MongoDB connection string: " + jdbcUrl, iae);
}
var database = this.connectionString.getDatabase();
var database = connectionString.getDatabase();
if (database == null) {
throw new ConfigurationException(JAKARTA_JDBC_URL, "database must be provided");
throw new HibernateException("database must be provided in connection string: " + jdbcUrl);
}
this.database = database;
this.user = ConfigurationHelper.getOptionalConfiguration(configurationValues, JAKARTA_JDBC_USER);
this.password = ConfigurationHelper.getOptionalConfiguration(configurationValues, JAKARTA_JDBC_PASSWORD);
}

@Override
public void start() {
// connectionString and database are set as mandatory values in the above configure method
// if either is unset, exception would have been thrown and this method invocation would have be skipped
castNonNull(this.connectionString);
castNonNull(this.database);

var clientSettingsBuilder = MongoClientSettings.builder().applyConnectionString(connectionString);
if (this.user != null) {
var password = this.password == null ? null : this.password.toCharArray();
var credential = MongoCredential.createCredential(this.user, this.database, password);
clientSettingsBuilder.credential(credential);
}

var codecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry());
clientSettingsBuilder.codecRegistry(codecRegistry);
if (configValues.get(JAKARTA_JDBC_USER) != null || configValues.get(JAKARTA_JDBC_PASSWORD) != null) {
throw new NotYetImplementedException("to be implemented after auth could be tested in CI");
}

var clientSettings = clientSettingsBuilder.build();
NathanQingyangXu marked this conversation as resolved.
Show resolved Hide resolved
jyemin marked this conversation as resolved.
Show resolved Hide resolved
this.mongoClient = MongoClients.create(clientSettings);
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/mongodb/hibernate/package-info.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* 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.
*/

@NullMarked
jyemin marked this conversation as resolved.
Show resolved Hide resolved
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
package com.mongodb.hibernate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_URL;
import static org.junit.jupiter.api.Assertions.*;

import com.mongodb.hibernate.exception.ConfigurationException;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.spi.ServiceException;
Expand All @@ -38,14 +38,14 @@ void test_invalid_connection_String() {
var exception = assertThrows(
ServiceException.class,
() -> buildSessionFactory(Map.of(JAKARTA_JDBC_URL, "jdbc:postgresql://localhost/test")));
assertInstanceOf(ConfigurationException.class, exception.getCause());
assertInstanceOf(HibernateException.class, exception.getCause());
}

@Test
void test_when_database_absent() {
var exception = assertThrows(
ServiceException.class, () -> buildSessionFactory(Map.of(JAKARTA_JDBC_URL, "mongodb://localhost")));
assertInstanceOf(ConfigurationException.class, exception.getCause());
assertInstanceOf(HibernateException.class, exception.getCause());
}

private void buildSessionFactory(Map<String, String> configurationValues) throws ServiceException {
Expand Down
4 changes: 0 additions & 4 deletions src/test/java/com/mongodb/hibernate/package-info.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/test/resources/hibernate.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
jakarta.persistence.jdbc.url=mongodb://localhost/test
jakarta.persistence.jdbc.url=mongodb://localhost/mongo-hibernate-test?directConnection=false
hibernate.dialect=com.mongodb.hibernate.dialect.MongoDialect
hibernate.connection.provider_class=com.mongodb.hibernate.jdbc.MongoConnectionProvider