Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5c70ee8
implement java.sql.PreparedStatement
NathanQingyangXu Dec 16, 2024
68bd2ac
disable errorprone scanning on test code (so we can get rid of annoyi…
NathanQingyangXu Jan 16, 2025
88e9d0a
suppress errorprone warnings regarding the usage of java.util.Date in…
NathanQingyangXu Jan 16, 2025
c479bf1
improve MongoPreparedStatementIntegrationTests by making it more complex
NathanQingyangXu Jan 16, 2025
4ea1aae
simplify logic after references to other JDBC implementation
NathanQingyangXu Jan 16, 2025
5c63de4
method renaming to improve readability
NathanQingyangXu Jan 20, 2025
6a1bf55
remove 'protected' visibility keyword usage on MongoStatement#execute…
NathanQingyangXu Jan 20, 2025
bc32a4c
made all JDBC adapters as interfaces
NathanQingyangXu Jan 20, 2025
43e5a2b
add checkSqlTypeSupported() method to MongoPreparedStatement
NathanQingyangXu Jan 20, 2025
62c76a0
add fail() in MongoPreparedStatement#parseParameters(BsonValue,List)
NathanQingyangXu Jan 20, 2025
a34dbf2
rename parameters to parameterValueSetters
NathanQingyangXu Jan 20, 2025
82b6194
fix the wrong usag of fail() in MongoPreparedStatement
NathanQingyangXu Jan 20, 2025
b19d26d
get rid of stream usage in MongoPreparedStatement
NathanQingyangXu Jan 20, 2025
203d05f
remove exception message format constant usage in MongoPreparedStatement
NathanQingyangXu Jan 20, 2025
b978c8e
use static import for VisibleForTesting in MongoPreparedStatement
NathanQingyangXu Jan 20, 2025
75db3d9
move checkClosed to public methods instead of common private method
NathanQingyangXu Jan 20, 2025
17b91ac
make null value setting go through centralized setNull() method
NathanQingyangXu Jan 20, 2025
2e45866
make time setter invoke other overloaded methods accepting nullable C…
NathanQingyangXu Jan 20, 2025
62862f5
Update src/integrationTest/java/com/mongodb/hibernate/jdbc/MongoPrepa…
NathanQingyangXu Jan 20, 2025
d4f691a
remove unnecessary @MockitoSettings(strictness = Strictness.WARN) in …
NathanQingyangXu Jan 20, 2025
17dd554
remove unnecessary ParameterParsingTests in MongoPreparedStatementTests
NathanQingyangXu Jan 20, 2025
46ecaaf
fix erroneous usage of fail in MongoPreparedStatement
NathanQingyangXu Jan 21, 2025
5609f0f
rename `parameters` method arguments consistently in MongoPreparedSta…
NathanQingyangXu Jan 21, 2025
d3a10bd
add checking explicitly at the beginning of each public method regard…
NathanQingyangXu Jan 21, 2025
9ed6736
implement set time methods accepting Calendar parameter
NathanQingyangXu Jan 21, 2025
d93dfdc
change setNull() for user-typed overloaded method to throw NotYetImpl…
NathanQingyangXu Jan 21, 2025
08916c1
refactor setNull() as per Javadoc; improve checkParameterIndex() logic
NathanQingyangXu Jan 22, 2025
96692d6
refactor setNull() as per Javadoc; improve checkParameterIndex() logic
NathanQingyangXu Jan 22, 2025
939424b
use global configuration to simplify mock answer smart nullness config
NathanQingyangXu Jan 22, 2025
cc10c1b
remove Blob and Clob support
NathanQingyangXu Jan 23, 2025
55b223d
add isWrapper() implementation to be consistent with metadata PR
NathanQingyangXu Jan 23, 2025
412f38f
minor change to PreparedStatementAdapter's javadoc (removing 'by defa…
NathanQingyangXu Jan 23, 2025
d1c4006
make time related stuff to be implemented in their own ticket
NathanQingyangXu Jan 24, 2025
1073bf0
improve fail() message in MongoPreparedStatement#parseParameters
NathanQingyangXu Jan 24, 2025
89429ab
Update src/test/java/org/mockito/configuration/MockitoConfiguration.java
NathanQingyangXu Jan 24, 2025
c71b5d5
remove unnecessary RETURNS_SMART_NULLS from MongoPreparedStatementTes…
NathanQingyangXu Jan 24, 2025
30004df
add isWrapperFor for MongoConnection and MongoStatement
NathanQingyangXu Jan 24, 2025
bdad28d
Update src/main/java/com/mongodb/hibernate/jdbc/MongoStatement.java
NathanQingyangXu Jan 27, 2025
038ec75
Merge branch 'main' into HIBERNATE-13-preparedStatement
NathanQingyangXu Jan 27, 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
6 changes: 4 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,17 @@ tasks.withType<JavaCompile>().configureEach {
options.errorprone {
disableWarningsInGeneratedCode.set(true)
option("NullAway:AnnotatedPackages", "com.mongodb.hibernate")
option("NullAway:ExcludedFieldAnnotations", "org.mockito.Mock")
option("NullAway:ExcludedFieldAnnotations", "org.mockito.InjectMocks")
}
}
tasks.compileJava {
// The check defaults to a warning, bump it up to an error for the main sources
options.errorprone.error("NullAway")
}

tasks.compileTestJava {
options.errorprone.isEnabled.set(false)
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Build Config

Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ google-errorprone-core = "2.36.0"
nullaway = "0.12.2"
jspecify = "1.0.0"
hibernate-core = "6.6.4.Final"
mongo-java-driver-sync = "5.2.1"
mongo-java-driver-sync = "5.3.0"
slf4j-api = "2.0.16"
logback-classic = "1.5.15"
mockito = "5.14.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* 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.jdbc;

import static com.mongodb.hibernate.internal.MongoAssertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import org.bson.BsonDocument;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class MongoPreparedStatementIntegrationTests {

private static @Nullable SessionFactory sessionFactory;

private @Nullable Session session;

@BeforeAll
static void beforeAll() {
sessionFactory = new Configuration().buildSessionFactory();
}

@AfterAll
static void afterAll() {
if (sessionFactory != null) {
sessionFactory.close();
}
}

@BeforeEach
void setUp() {
session = assertNotNull(sessionFactory).openSession();
}

@AfterEach
void tearDown() {
if (session != null) {
session.close();
}
}

@Nested
class ExecuteUpdateTests {

@BeforeEach
void setUp() {
assertNotNull(session).doWork(conn -> {
conn.createStatement()
.executeUpdate(
"""
{
delete: "books",
deletes: [
{ q: {}, limit: 0 }
]
}""");
});
}

private static final String INSERT_MQL =
"""
{
insert: "books",
documents: [
{
_id: 1,
title: "War and Peace",
author: "Leo Tolstoy",
outOfStock: false,
tags: [ "classic", "tolstoy" ]
},
{
_id: 2,
title: "Anna Karenina",
author: "Leo Tolstoy",
outOfStock: false,
tags: [ "classic", "tolstoy" ]
},
{
_id: 3,
title: "Crime and Punishment",
author: "Fyodor Dostoevsky",
outOfStock: false,
tags: [ "classic", "Dostoevsky", "literature" ]
}
]
}""";

@ParameterizedTest
@ValueSource(booleans = {true, false})
void testUpdate(boolean autoCommit) {
// given
prepareData();

// when && then
var expectedDocs = Set.of(
BsonDocument.parse(
"""
{
_id: 1,
title: "War and Peace",
author: "Leo Tolstoy",
outOfStock: true,
tags: [ "classic", "tolstoy", "literature" ]
}"""),
BsonDocument.parse(
"""
{
_id: 2,
title: "Anna Karenina",
author: "Leo Tolstoy",
outOfStock: true,
tags: [ "classic", "tolstoy", "literature" ]
}"""),
BsonDocument.parse(
"""
{
_id: 3,
title: "Crime and Punishment",
author: "Fyodor Dostoevsky",
outOfStock: false,
tags: [ "classic", "Dostoevsky", "literature" ]
}"""));
Function<Connection, MongoPreparedStatement> pstmtProvider = connection -> {
try {
var pstmt = (MongoPreparedStatement)
connection.prepareStatement(
"""
{
update: "books",
updates: [
{
q: { author: { $undefined: true } },
u: {
$set: {
outOfStock: { $undefined: true }
},
$push: { tags: { $undefined: true } }
},
multi: true
}
]
}""");
pstmt.setString(1, "Leo Tolstoy");
pstmt.setBoolean(2, true);
pstmt.setString(3, "literature");
return pstmt;
} catch (SQLException e) {
throw new RuntimeException(e);
}
};
assertExecuteUpdate(pstmtProvider, autoCommit, 2, expectedDocs);
}

private void prepareData() {
assertNotNull(session).doWork(connection -> {
connection.setAutoCommit(true);
var statement = connection.createStatement();
statement.executeUpdate(INSERT_MQL);
});
}

private void assertExecuteUpdate(
Function<Connection, MongoPreparedStatement> pstmtProvider,
boolean autoCommit,
int expectedUpdatedRowCount,
Set<? extends BsonDocument> expectedDocuments) {
assertNotNull(session).doWork(connection -> {
connection.setAutoCommit(autoCommit);
try (var pstmt = pstmtProvider.apply(connection)) {
try {
assertEquals(expectedUpdatedRowCount, pstmt.executeUpdate());
} finally {
if (!autoCommit) {
connection.commit();
}
}
var realDocuments = pstmt.getMongoDatabase()
.getCollection("books", BsonDocument.class)
.find()
.into(new HashSet<>());
assertEquals(expectedDocuments, realDocuments);
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,20 +238,20 @@ private void assertExecuteUpdate(
String mql, boolean autoCommit, int expectedRowCount, Set<? extends BsonDocument> expectedDocuments) {
assertNotNull(session).doWork(connection -> {
connection.setAutoCommit(autoCommit);
var statement = (MongoStatement) connection.createStatement();
try {
assertEquals(expectedRowCount, statement.executeUpdate(mql));
} finally {
if (!autoCommit) {
connection.commit();
try (var stmt = (MongoStatement) connection.createStatement()) {
try {
assertEquals(expectedRowCount, stmt.executeUpdate(mql));
} finally {
if (!autoCommit) {
connection.commit();
}
}
var realDocuments = stmt.getMongoDatabase()
.getCollection("books", BsonDocument.class)
.find()
.into(new HashSet<>());
assertEquals(expectedDocuments, realDocuments);
}
var realDocuments = statement
.getMongoDatabase()
.getCollection("books", BsonDocument.class)
.find()
.into(new HashSet<>());
assertEquals(expectedDocuments, realDocuments);
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,16 @@ public static <T> T assertNotNull(@Nullable T value) throws AssertionError {
}
return value;
}

/**
* Asserts that failure happens invariably.
*
* @param msg The failure message.
* @return Never completes normally. The return type is {@link AssertionError} to allow writing {@code throw
* fail("failure message")}. This may be helpful in non-{@code void} methods.
* @throws AssertionError Always
*/
public static AssertionError fail(String msg) throws AssertionError {
throw new AssertionError(assertNotNull(msg));
}
}
Loading