diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2e091a53..36e346fb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ assertj = "3.27.3" google-errorprone-core = "2.36.0" nullaway = "0.12.4" jspecify = "1.0.0" -hibernate-orm = "6.6.9.Final" +hibernate-orm = "6.6.23.Final" mongo-java-driver-sync = "5.3.1" slf4j-api = "2.0.16" logback-classic = "1.5.16" diff --git a/src/integrationTest/java/com/mongodb/hibernate/ArrayAndCollectionIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/ArrayAndCollectionIntegrationTests.java index 868bb9c8..6c2b743d 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/ArrayAndCollectionIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/ArrayAndCollectionIntegrationTests.java @@ -17,6 +17,7 @@ package com.mongodb.hibernate; import static com.mongodb.hibernate.MongoTestAssertions.assertEq; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hibernate.cfg.AvailableSettings.WRAPPER_ARRAY_HANDLING; @@ -47,7 +48,6 @@ import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScopeAware; import org.hibernate.testing.orm.junit.Setting; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -78,33 +78,32 @@ public void injectSessionFactoryScope(SessionFactoryScope sessionFactoryScope) { void testArrayAndCollectionValues() { var item = new ItemWithArrayAndCollectionValues( 1, - // TODO-HIBERNATE-48 sprinkle on `null` array/collection elements new byte[] {2, 3}, new char[] {'s', 't', 'r'}, new int[] {5}, new long[] {Long.MAX_VALUE, 6}, new double[] {Double.MAX_VALUE}, new boolean[] {true}, - new Character[] {'s', 't', 'r'}, - new Integer[] {7}, - new Long[] {8L}, - new Double[] {9.1d}, - new Boolean[] {true}, - new String[] {"str"}, - new BigDecimal[] {BigDecimal.valueOf(10.1)}, - new ObjectId[] {new ObjectId("000000000000000000000001")}, + new Character[] {'s', null, 't', 'r'}, + new Integer[] {null, 7}, + new Long[] {8L, null}, + new Double[] {9.1d, null}, + new Boolean[] {true, null}, + new String[] {null, "str"}, + new BigDecimal[] {null, BigDecimal.valueOf(10.1)}, + new ObjectId[] {new ObjectId("000000000000000000000001"), null}, new StructAggregateEmbeddableIntegrationTests.Single[] { - new StructAggregateEmbeddableIntegrationTests.Single(1) + new StructAggregateEmbeddableIntegrationTests.Single(1), null }, - List.of('s', 't', 'r'), - List.of(5), - List.of(Long.MAX_VALUE, 6L), - List.of(Double.MAX_VALUE), - List.of(true), - List.of("str"), - List.of(BigDecimal.valueOf(10.1)), - List.of(new ObjectId("000000000000000000000001")), - List.of(new StructAggregateEmbeddableIntegrationTests.Single(1))); + asList('s', 't', null, 'r'), + asList(null, 5), + asList(Long.MAX_VALUE, null, 6L), + asList(null, Double.MAX_VALUE), + asList(null, true), + asList("str", null), + asList(BigDecimal.valueOf(10.1), null), + asList(null, new ObjectId("000000000000000000000001")), + asList(new StructAggregateEmbeddableIntegrationTests.Single(1), null)); sessionFactoryScope.inTransaction(session -> session.persist(item)); assertCollectionContainsExactly( """ @@ -116,24 +115,24 @@ void testArrayAndCollectionValues() { longs: [{$numberLong: "9223372036854775807"}, {$numberLong: "6"}], doubles: [{$numberDouble: "1.7976931348623157E308"}], booleans: [true], - boxedChars: ["s", "t", "r"], - boxedInts: [7], - boxedLongs: [{$numberLong: "8"}], - boxedDoubles: [{$numberDouble: "9.1"}], - boxedBooleans: [true], - strings: ["str"], - bigDecimals: [{$numberDecimal: "10.1"}], - objectIds: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddables: [{a: 1}], - charsCollection: ["s", "t", "r"], - intsCollection: [5], - longsCollection: [{$numberLong: "9223372036854775807"}, {$numberLong: "6"}], - doublesCollection: [{$numberDouble: "1.7976931348623157E308"}], - booleansCollection: [true], - stringsCollection: ["str"], - bigDecimalsCollection: [{$numberDecimal: "10.1"}], - objectIdsCollection: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddablesCollection: [{a: 1}] + boxedChars: ["s", null, "t", "r"], + boxedInts: [null, 7], + boxedLongs: [{$numberLong: "8"}, null], + boxedDoubles: [{$numberDouble: "9.1"}, null], + boxedBooleans: [true, null], + strings: [null, "str"], + bigDecimals: [null, {$numberDecimal: "10.1"}], + objectIds: [{$oid: "000000000000000000000001"}, null], + structAggregateEmbeddables: [{a: 1}, null], + charsCollection: ["s", "t", null, "r"], + intsCollection: [null, 5], + longsCollection: [{$numberLong: "9223372036854775807"}, null, {$numberLong: "6"}], + doublesCollection: [null, {$numberDouble: "1.7976931348623157E308"}], + booleansCollection: [null, true], + stringsCollection: ["str", null], + bigDecimalsCollection: [{$numberDecimal: "10.1"}, null], + objectIdsCollection: [null, {$oid: "000000000000000000000001"}], + structAggregateEmbeddablesCollection: [{a: 1}, null] } """); var loadedItem = sessionFactoryScope.fromTransaction( @@ -158,24 +157,24 @@ void testArrayAndCollectionValues() { longs: [{$numberLong: "9223372036854775807"}, {$numberLong: "-6"}], doubles: [{$numberDouble: "1.7976931348623157E308"}], booleans: [true], - boxedChars: ["s", "t", "r"], - boxedInts: [7], - boxedLongs: [{$numberLong: "8"}], - boxedDoubles: [{$numberDouble: "9.1"}], - boxedBooleans: [true], - strings: ["str"], - bigDecimals: [{$numberDecimal: "10.1"}], - objectIds: [{$oid: "000000000000000000000002"}], - structAggregateEmbeddables: [{a: 1}], - charsCollection: ["s", "t", "r"], - intsCollection: [5], - longsCollection: [{$numberLong: "9223372036854775807"}, {$numberLong: "-6"}], - doublesCollection: [{$numberDouble: "1.7976931348623157E308"}], - booleansCollection: [true], - stringsCollection: ["str"], - bigDecimalsCollection: [{$numberDecimal: "10.1"}], - objectIdsCollection: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddablesCollection: [{a: 1}] + boxedChars: ["s", null, "t", "r"], + boxedInts: [null, 7], + boxedLongs: [{$numberLong: "8"}, null], + boxedDoubles: [{$numberDouble: "9.1"}, null], + boxedBooleans: [true, null], + strings: [null, "str"], + bigDecimals: [null, {$numberDecimal: "10.1"}], + objectIds: [{$oid: "000000000000000000000002"}, null], + structAggregateEmbeddables: [{a: 1}, null], + charsCollection: ["s", "t", null, "r"], + intsCollection: [null, 5], + longsCollection: [{$numberLong: "9223372036854775807"}, null, {$numberLong: "-6"}], + doublesCollection: [null, {$numberDouble: "1.7976931348623157E308"}], + booleansCollection: [null, true], + stringsCollection: ["str", null], + bigDecimalsCollection: [{$numberDecimal: "10.1"}, null], + objectIdsCollection: [null, {$oid: "000000000000000000000001"}], + structAggregateEmbeddablesCollection: [{a: 1}, null] } """); loadedItem = sessionFactoryScope.fromTransaction( @@ -248,7 +247,6 @@ void testArrayAndCollectionEmptyValues() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testArrayAndCollectionNullValues() { var item = new ItemWithArrayAndCollectionValues( 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -410,7 +408,6 @@ void testArrayAndCollectionValuesOfStructAggregateEmbeddablesHavingArraysAndColl * @see StructAggregateEmbeddableIntegrationTests#testNestedValueHavingNullArraysAndCollections() */ @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") public void testArrayAndCollectionValuesOfEmptyStructAggregateEmbeddables() { var emptyStructAggregateEmbeddable = new ArraysAndCollections( null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -597,16 +594,12 @@ static class ItemWithArrayAndCollectionValuesOfStructAggregateEmbeddablesHavingA @Nested class Unsupported { - /** - * The {@link ClassCastException} caught here manifests a Hibernate ORM bug. The issue goes away if the - * {@link ItemWithBoxedBytesArrayValue#bytes} field is removed. Otherwise, the behavior of this test should have - * been equivalent to {@link #testBytesCollectionValue()}. - */ + @Test void testBoxedBytesArrayValue() { var item = new ItemWithBoxedBytesArrayValue(1, new byte[] {1}, new Byte[] {2}); assertThatThrownBy(() -> sessionFactoryScope.inTransaction(session -> session.persist(item))) - .isInstanceOf(ClassCastException.class); + .hasCauseInstanceOf(SQLFeatureNotSupportedException.class); } @Test diff --git a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java index f470d0fa..359f8624 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java @@ -32,7 +32,6 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScopeAware; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -97,7 +96,6 @@ void testSimpleEntityInsertion() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testEntityWithNullFieldValuesInsertion() { sessionFactoryScope.inTransaction(session -> session.persist(new Item( 1, @@ -218,7 +216,6 @@ void testSimpleUpdate() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testSimpleUpdateWithNullFieldValues() { sessionFactoryScope.inTransaction(session -> { var item = new Item( @@ -290,7 +287,6 @@ void testDynamicUpdate() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testDynamicUpdateWithNullFieldValues() { sessionFactoryScope.inTransaction(session -> { var item = new ItemDynamicallyUpdated(1, false, true); @@ -337,7 +333,6 @@ void testFindByPrimaryKey() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testFindByPrimaryKeyWithNullFieldValues() { var item = new Item( 1, 'c', 1, Long.MAX_VALUE, Double.MAX_VALUE, true, null, null, null, null, null, null, null, null); diff --git a/src/integrationTest/java/com/mongodb/hibernate/embeddable/EmbeddableIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/embeddable/EmbeddableIntegrationTests.java index 58a725ad..ff35df84 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/embeddable/EmbeddableIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/embeddable/EmbeddableIntegrationTests.java @@ -18,6 +18,7 @@ import static com.mongodb.hibernate.MongoTestAssertions.assertEq; import static com.mongodb.hibernate.MongoTestAssertions.assertUsingRecursiveComparison; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertNull; @@ -36,6 +37,7 @@ import jakarta.persistence.Table; import java.math.BigDecimal; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -48,7 +50,6 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScopeAware; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -151,7 +152,6 @@ void testFlattenedValues() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testFlattenedNullValueOrHavingNulls() { var item = new ItemWithFlattenedValues( new Single(1), @@ -234,33 +234,32 @@ void testFlattenedValueHavingArraysAndCollections() { var item = new ItemWithFlattenedValueHavingArraysAndCollections( 1, new ArraysAndCollections( - // TODO-HIBERNATE-48 sprinkle on `null` array/collection elements new byte[] {2, 3}, new char[] {'s', 't', 'r'}, new int[] {5}, new long[] {Long.MAX_VALUE, 6}, new double[] {Double.MAX_VALUE}, new boolean[] {true}, - new Character[] {'s', 't', 'r'}, - new Integer[] {7}, - new Long[] {8L}, - new Double[] {9.1d}, - new Boolean[] {true}, - new String[] {"str"}, - new BigDecimal[] {BigDecimal.valueOf(10.1)}, - new ObjectId[] {new ObjectId("000000000000000000000001")}, + new Character[] {'s', null, 't', 'r'}, + new Integer[] {null, 7}, + new Long[] {8L, null}, + new Double[] {9.1d, null}, + new Boolean[] {true, null}, + new String[] {null, "str"}, + new BigDecimal[] {null, BigDecimal.valueOf(10.1)}, + new ObjectId[] {new ObjectId("000000000000000000000001"), null}, new StructAggregateEmbeddableIntegrationTests.Single[] { - new StructAggregateEmbeddableIntegrationTests.Single(1) + new StructAggregateEmbeddableIntegrationTests.Single(1), null }, - List.of('s', 't', 'r'), - Set.of(5), - List.of(Long.MAX_VALUE, 6L), - List.of(Double.MAX_VALUE), - List.of(true), - List.of("str"), - List.of(BigDecimal.valueOf(10.1)), - List.of(new ObjectId("000000000000000000000001")), - List.of(new StructAggregateEmbeddableIntegrationTests.Single(1)))); + asList('s', 't', null, 'r'), + new HashSet<>(asList(null, 5)), + asList(Long.MAX_VALUE, null, 6L), + asList(null, Double.MAX_VALUE), + asList(null, true), + asList("str", null), + asList(BigDecimal.valueOf(10.1), null), + asList(null, new ObjectId("000000000000000000000001")), + asList(new StructAggregateEmbeddableIntegrationTests.Single(1), null))); sessionFactoryScope.inTransaction(session -> session.persist(item)); assertCollectionContainsExactly( """ @@ -272,24 +271,24 @@ void testFlattenedValueHavingArraysAndCollections() { longs: [{$numberLong: "9223372036854775807"}, {$numberLong: "6"}], doubles: [{$numberDouble: "1.7976931348623157E308"}], booleans: [true], - boxedChars: ["s", "t", "r"], - boxedInts: [7], - boxedLongs: [{$numberLong: "8"}], - boxedDoubles: [{$numberDouble: "9.1"}], - boxedBooleans: [true], - strings: ["str"], - bigDecimals: [{$numberDecimal: "10.1"}], - objectIds: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddables: [{a: 1}], - charsCollection: ["s", "t", "r"], - intsCollection: [5], - longsCollection: [{$numberLong: "9223372036854775807"}, {$numberLong: "6"}], - doublesCollection: [{$numberDouble: "1.7976931348623157E308"}], - booleansCollection: [true], - stringsCollection: ["str"], - bigDecimalsCollection: [{$numberDecimal: "10.1"}], - objectIdsCollection: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddablesCollection: [{a: 1}] + boxedChars: ["s", null, "t", "r"], + boxedInts: [null, 7], + boxedLongs: [{$numberLong: "8"}, null], + boxedDoubles: [{$numberDouble: "9.1"}, null], + boxedBooleans: [true, null], + strings: [null, "str"], + bigDecimals: [null, {$numberDecimal: "10.1"}], + objectIds: [{$oid: "000000000000000000000001"}, null], + structAggregateEmbeddables: [{a: 1}, null], + charsCollection: ["s", "t", null, "r"], + intsCollection: [null, 5], + longsCollection: [{$numberLong: "9223372036854775807"}, null, {$numberLong: "6"}], + doublesCollection: [null, {$numberDouble: "1.7976931348623157E308"}], + booleansCollection: [null, true], + stringsCollection: ["str", null], + bigDecimalsCollection: [{$numberDecimal: "10.1"}, null], + objectIdsCollection: [null, {$oid: "000000000000000000000001"}], + structAggregateEmbeddablesCollection: [{a: 1}, null] } """); var loadedItem = sessionFactoryScope.fromTransaction( @@ -314,24 +313,24 @@ void testFlattenedValueHavingArraysAndCollections() { longs: [{$numberLong: "9223372036854775807"}, {$numberLong: "-6"}], doubles: [{$numberDouble: "1.7976931348623157E308"}], booleans: [true], - boxedChars: ["s", "t", "r"], - boxedInts: [7], - boxedLongs: [{$numberLong: "8"}], - boxedDoubles: [{$numberDouble: "9.1"}], - boxedBooleans: [true], - strings: ["str"], - bigDecimals: [{$numberDecimal: "10.1"}], - objectIds: [{$oid: "000000000000000000000002"}], - structAggregateEmbeddables: [{a: 1}], - charsCollection: ["s", "t", "r"], - intsCollection: [5], - longsCollection: [{$numberLong: "9223372036854775807"}, {$numberLong: "-6"}], - doublesCollection: [{$numberDouble: "1.7976931348623157E308"}], - booleansCollection: [true], - stringsCollection: ["str"], - bigDecimalsCollection: [{$numberDecimal: "10.1"}], - objectIdsCollection: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddablesCollection: [{a: 1}] + boxedChars: ["s", null, "t", "r"], + boxedInts: [null, 7], + boxedLongs: [{$numberLong: "8"}, null], + boxedDoubles: [{$numberDouble: "9.1"}, null], + boxedBooleans: [true, null], + strings: [null, "str"], + bigDecimals: [null, {$numberDecimal: "10.1"}], + objectIds: [{$oid: "000000000000000000000002"}, null], + structAggregateEmbeddables: [{a: 1}, null], + charsCollection: ["s", "t", null, "r"], + intsCollection: [null, 5], + longsCollection: [{$numberLong: "9223372036854775807"}, null, {$numberLong: "-6"}], + doublesCollection: [null, {$numberDouble: "1.7976931348623157E308"}], + booleansCollection: [null, true], + stringsCollection: ["str", null], + bigDecimalsCollection: [{$numberDecimal: "10.1"}, null], + objectIdsCollection: [null, {$oid: "000000000000000000000001"}], + structAggregateEmbeddablesCollection: [{a: 1}, null] } """); loadedItem = sessionFactoryScope.fromTransaction( @@ -412,7 +411,6 @@ void testFlattenedValueHavingEmptyArraysAndCollections() { * @see ArrayAndCollectionIntegrationTests#testArrayAndCollectionValuesOfEmptyStructAggregateEmbeddables() */ @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") public void testFlattenedValueHavingNullArraysAndCollections() { var emptyEmbeddable = new ArraysAndCollections( null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -460,7 +458,6 @@ public void testFlattenedValueHavingNullArraysAndCollections() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testReadNestedValuesMissingFields() { var insertResult = mongoCollection.insertOne( BsonDocument.parse( diff --git a/src/integrationTest/java/com/mongodb/hibernate/embeddable/StructAggregateEmbeddableIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/embeddable/StructAggregateEmbeddableIntegrationTests.java index e42bb6d3..2d9d86a6 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/embeddable/StructAggregateEmbeddableIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/embeddable/StructAggregateEmbeddableIntegrationTests.java @@ -17,10 +17,9 @@ package com.mongodb.hibernate.embeddable; import static com.mongodb.hibernate.MongoTestAssertions.assertEq; -import static com.mongodb.hibernate.MongoTestAssertions.assertUsingRecursiveComparison; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertNull; import com.mongodb.client.MongoCollection; import com.mongodb.hibernate.ArrayAndCollectionIntegrationTests; @@ -36,6 +35,7 @@ import java.math.BigDecimal; import java.sql.SQLFeatureNotSupportedException; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; import org.bson.BsonDocument; @@ -47,7 +47,6 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScopeAware; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -164,7 +163,6 @@ void testNestedValues() { } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testNestedNullValueOrHavingNulls() { var item = new ItemWithNestedValues( new EmbeddableIntegrationTests.Single(1), @@ -240,7 +238,6 @@ void testNestedNullValueOrHavingNulls() { void testNestedValueHavingArraysAndCollections() { var item = new ItemWithNestedValueHavingArraysAndCollections( 1, - // TODO-HIBERNATE-48 sprinkle on `null` array/collection elements new ArraysAndCollections( new byte[] {2, 3}, new char[] {'s', 't', 'r'}, @@ -248,24 +245,24 @@ void testNestedValueHavingArraysAndCollections() { new long[] {Long.MAX_VALUE, 6}, new double[] {Double.MAX_VALUE}, new boolean[] {true}, - new Character[] {'s', 't', 'r'}, - new Integer[] {7}, - new Long[] {8L}, - new Double[] {9.1d}, - new Boolean[] {true}, - new String[] {"str"}, - new BigDecimal[] {BigDecimal.valueOf(10.1)}, - new ObjectId[] {new ObjectId("000000000000000000000001")}, - new Single[] {new Single(1)}, - List.of('s', 't', 'r'), - Set.of(5), - List.of(Long.MAX_VALUE, 6L), - List.of(Double.MAX_VALUE), - List.of(true), - List.of("str"), - List.of(BigDecimal.valueOf(10.1)), - List.of(new ObjectId("000000000000000000000001")), - List.of(new Single(1)))); + new Character[] {'s', null, 't', 'r'}, + new Integer[] {null, 7}, + new Long[] {8L, null}, + new Double[] {9.1d, null}, + new Boolean[] {true, null}, + new String[] {null, "str"}, + new BigDecimal[] {null, BigDecimal.valueOf(10.1)}, + new ObjectId[] {new ObjectId("000000000000000000000001"), null}, + new Single[] {new Single(1), null}, + asList('s', 't', null, 'r'), + new HashSet<>(asList(null, 5)), + asList(Long.MAX_VALUE, null, 6L), + asList(null, Double.MAX_VALUE), + asList(null, true), + asList("str", null), + asList(BigDecimal.valueOf(10.1), null), + asList(null, new ObjectId("000000000000000000000001")), + asList(new Single(1), null))); sessionFactoryScope.inTransaction(session -> session.persist(item)); assertCollectionContainsExactly( """ @@ -278,24 +275,24 @@ void testNestedValueHavingArraysAndCollections() { longs: [{$numberLong: "9223372036854775807"}, {$numberLong: "6"}], doubles: [{$numberDouble: "1.7976931348623157E308"}], booleans: [true], - boxedChars: ["s", "t", "r"], - boxedInts: [7], - boxedLongs: [{$numberLong: "8"}], - boxedDoubles: [{$numberDouble: "9.1"}], - boxedBooleans: [true], - strings: ["str"], - bigDecimals: [{$numberDecimal: "10.1"}], - objectIds: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddables: [{a: 1}], - charsCollection: ["s", "t", "r"], - intsCollection: [5], - longsCollection: [{$numberLong: "9223372036854775807"}, {$numberLong: "6"}], - doublesCollection: [{$numberDouble: "1.7976931348623157E308"}], - booleansCollection: [true], - stringsCollection: ["str"], - bigDecimalsCollection: [{$numberDecimal: "10.1"}], - objectIdsCollection: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddablesCollection: [{a: 1}] + boxedChars: ["s", null, "t", "r"], + boxedInts: [null, 7], + boxedLongs: [{$numberLong: "8"}, null], + boxedDoubles: [{$numberDouble: "9.1"}, null], + boxedBooleans: [true, null], + strings: [null, "str"], + bigDecimals: [null, {$numberDecimal: "10.1"}], + objectIds: [{$oid: "000000000000000000000001"}, null], + structAggregateEmbeddables: [{a: 1}, null], + charsCollection: ["s", "t", null, "r"], + intsCollection: [null, 5], + longsCollection: [{$numberLong: "9223372036854775807"}, null, {$numberLong: "6"}], + doublesCollection: [null, {$numberDouble: "1.7976931348623157E308"}], + booleansCollection: [null, true], + stringsCollection: ["str", null], + bigDecimalsCollection: [{$numberDecimal: "10.1"}, null], + objectIdsCollection: [null, {$oid: "000000000000000000000001"}], + structAggregateEmbeddablesCollection: [{a: 1}, null] } } """); @@ -322,24 +319,24 @@ void testNestedValueHavingArraysAndCollections() { longs: [{$numberLong: "9223372036854775807"}, {$numberLong: "-6"}], doubles: [{$numberDouble: "1.7976931348623157E308"}], booleans: [true], - boxedChars: ["s", "t", "r"], - boxedInts: [7], - boxedLongs: [{$numberLong: "8"}], - boxedDoubles: [{$numberDouble: "9.1"}], - boxedBooleans: [true], - strings: ["str"], - bigDecimals: [{$numberDecimal: "10.1"}], - objectIds: [{$oid: "000000000000000000000002"}], - structAggregateEmbeddables: [{a: 1}], - charsCollection: ["s", "t", "r"], - intsCollection: [5], - longsCollection: [{$numberLong: "9223372036854775807"}, {$numberLong: "-6"}], - doublesCollection: [{$numberDouble: "1.7976931348623157E308"}], - booleansCollection: [true], - stringsCollection: ["str"], - bigDecimalsCollection: [{$numberDecimal: "10.1"}], - objectIdsCollection: [{$oid: "000000000000000000000001"}], - structAggregateEmbeddablesCollection: [{a: 1}] + boxedChars: ["s", null, "t", "r"], + boxedInts: [null, 7], + boxedLongs: [{$numberLong: "8"}, null], + boxedDoubles: [{$numberDouble: "9.1"}, null], + boxedBooleans: [true, null], + strings: [null, "str"], + bigDecimals: [null, {$numberDecimal: "10.1"}], + objectIds: [{$oid: "000000000000000000000002"}, null], + structAggregateEmbeddables: [{a: 1}, null], + charsCollection: ["s", "t", null, "r"], + intsCollection: [null, 5], + longsCollection: [{$numberLong: "9223372036854775807"}, null, {$numberLong: "-6"}], + doublesCollection: [null, {$numberDouble: "1.7976931348623157E308"}], + booleansCollection: [null, true], + stringsCollection: ["str", null], + bigDecimalsCollection: [{$numberDecimal: "10.1"}, null], + objectIdsCollection: [null, {$oid: "000000000000000000000001"}], + structAggregateEmbeddablesCollection: [{a: 1}, null] } } """); @@ -423,7 +420,6 @@ void testNestedValueHavingEmptyArraysAndCollections() { * @see ArrayAndCollectionIntegrationTests#testArrayAndCollectionValuesOfEmptyStructAggregateEmbeddables() */ @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") public void testNestedValueHavingNullArraysAndCollections() { var emptyStructAggregateEmbeddable = new ArraysAndCollections( null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -464,16 +460,10 @@ public void testNestedValueHavingNullArraysAndCollections() { """); var loadedItem = sessionFactoryScope.fromTransaction( session -> session.find(ItemWithNestedValueHavingArraysAndCollections.class, item.id)); - // `loadedItem.nested` is `null` despite `item.nested` not being `null`. - // There is nothing we can do here, such is the Hibernate ORM behavior. - assertNull(loadedItem.nested); - assertUsingRecursiveComparison(item, loadedItem, (assertion, expected) -> assertion - .ignoringFields("nested") - .isEqualTo(expected)); + assertEq(item, loadedItem); } @Test - @Disabled("TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 enable this test") void testReadNestedValuesMissingFields() { var insertResult = mongoCollection.insertOne( BsonDocument.parse( @@ -497,9 +487,7 @@ void testReadNestedValuesMissingFields() { insertResult.getInsertedId().asInt32().getValue()); var expectedItem = new ItemWithNestedValues( id, - // `loadedItem.nested1` is `null` despite `nested1` not being BSON `Null` in the database. - // There is nothing we can do here, such is the Hibernate ORM behavior. - null, + new Single(), new PairWithParent( 3, new Plural( @@ -547,11 +535,11 @@ static class ItemWithNestedValues { @Embeddable @Struct(name = "Single") public static class Single { - public int a; + public Integer a; Single() {} - public Single(int a) { + public Single(Integer a) { this.a = a; } } @@ -572,7 +560,7 @@ static class PairWithParent { } /** - * Hibernate ORM requires a getter for a {@link Parent} field, despite us using {@linkplain AccessType#FIELD + * Hibernate ORM requires a setter for a {@link Parent} field, despite us using {@linkplain AccessType#FIELD * field-based access}. */ void setParent(ItemWithNestedValues parent) { diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/Book.java b/src/integrationTest/java/com/mongodb/hibernate/query/Book.java index e4b9b3f8..df6caece 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/query/Book.java +++ b/src/integrationTest/java/com/mongodb/hibernate/query/Book.java @@ -29,13 +29,12 @@ public class Book { @Id public int id; - // TODO-HIBERNATE-48 dummy values are set for currently null value is not supported - public String title = ""; - public Boolean outOfStock = false; - public Integer publishYear = 0; - public Long isbn13 = 0L; - public Double discount = 0.0; - public BigDecimal price = new BigDecimal("0.0"); + public String title; + public Boolean outOfStock; + public Integer publishYear; + public Long isbn13; + public Double discount; + public BigDecimal price; public Book() {} @@ -45,4 +44,9 @@ public Book(int id, String title, Integer publishYear, Boolean outOfStock) { this.publishYear = publishYear; this.outOfStock = outOfStock; } + + @Override + public String toString() { + return "Book{" + "id=" + id + '}'; + } } diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/mutation/DeletionIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/query/mutation/DeletionIntegrationTests.java index 906966a5..7e4f1faf 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/query/mutation/DeletionIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/query/mutation/DeletionIntegrationTests.java @@ -76,9 +76,9 @@ void testDeletionWithNonZeroMutationCount() { "title": "Crime and Punishment", "outOfStock": false, "publishYear": 1866, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -88,9 +88,9 @@ void testDeletionWithNonZeroMutationCount() { "title": "Anna Karenina", "outOfStock": false, "publishYear": 1877, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -100,9 +100,9 @@ void testDeletionWithNonZeroMutationCount() { "title": "The Brothers Karamazov", "outOfStock": false, "publishYear": 1880, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """)), Set.of(Book.COLLECTION_NAME)); @@ -138,9 +138,9 @@ void testDeletionWithZeroMutationCount() { "title": "War and Peace", "outOfStock": true, "publishYear": 1869, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -150,9 +150,9 @@ void testDeletionWithZeroMutationCount() { "title": "Crime and Punishment", "outOfStock": false, "publishYear": 1866, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -162,9 +162,9 @@ void testDeletionWithZeroMutationCount() { "title": "Anna Karenina", "outOfStock": false, "publishYear": 1877, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -174,9 +174,9 @@ void testDeletionWithZeroMutationCount() { "title": "The Brothers Karamazov", "outOfStock": false, "publishYear": 1880, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -186,9 +186,9 @@ void testDeletionWithZeroMutationCount() { "title": "War and Peace", "outOfStock": false, "publishYear": 2025, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """)), Set.of(Book.COLLECTION_NAME)); diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/mutation/InsertionIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/query/mutation/InsertionIntegrationTests.java index 5a47f669..e7b4f3e5 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/query/mutation/InsertionIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/query/mutation/InsertionIntegrationTests.java @@ -43,7 +43,7 @@ void beforeEach() { @Test void testInsertPartialSingleDocument() { assertMutationQuery( - "insert into Book (id, title, outOfStock, isbn13, discount) values (1, 'Pride & Prejudice', false, 9780141439518L, 0.2D)", + "insert into Book (id, title, outOfStock, isbn13, discount) values (1, 'Pride & Prejudice', false, null, 0.2D)", null, 1, """ @@ -54,7 +54,7 @@ void testInsertPartialSingleDocument() { "_id": 1, "title": "Pride & Prejudice", "outOfStock": false, - "isbn13": 9780141439518, + "isbn13": null, "discount": 0.2 } ] @@ -68,7 +68,7 @@ void testInsertPartialSingleDocument() { "_id": 1, "title": "Pride & Prejudice", "outOfStock": false, - "isbn13": 9780141439518, + "isbn13": null, "discount": 0.2 } """)), @@ -78,7 +78,7 @@ void testInsertPartialSingleDocument() { @Test void testInsertSingleDocument() { assertMutationQuery( - "insert into Book (id, title, outOfStock, publishYear, isbn13, discount, price) values (1, 'Pride & Prejudice', false, 1813, 9780141439518L, 0.2D, 23.55BD)", + "insert into Book (id, title, outOfStock, publishYear, isbn13, discount, price) values (1, 'Pride & Prejudice', null, null, 9780141439518L, null, 23.55BD)", null, 1, """ @@ -88,10 +88,10 @@ void testInsertSingleDocument() { { "_id": 1, "title": "Pride & Prejudice", - "outOfStock": false, - "publishYear": 1813, + "outOfStock": null, + "publishYear": null, "isbn13": 9780141439518, - "discount": 0.2, + "discount": null, "price": {"$numberDecimal": "23.55"} } ] @@ -104,10 +104,10 @@ void testInsertSingleDocument() { { "_id": 1, "title": "Pride & Prejudice", - "outOfStock": false, - "publishYear": 1813, + "outOfStock": null, + "publishYear": null, "isbn13": 9780141439518, - "discount": 0.2, + "discount": null, "price": {"$numberDecimal": "23.55"} } """)), @@ -120,8 +120,8 @@ void testInsertMultipleDocuments() { """ insert into Book (id, title, outOfStock, publishYear, isbn13, discount, price) values - (1, 'Pride & Prejudice', false, 1813, 9780141439518L, 0.2D, 23.55BD), - (2, 'War & Peace', false, 1867, 9780143039990L, 0.1D, 19.99BD) + (1, null, false, null, 9780141439518L, null, 23.55BD), + (2, 'War & Peace', null, 1867, null, 0.1D, null) """, null, 2, @@ -131,21 +131,21 @@ insert into Book (id, title, outOfStock, publishYear, isbn13, discount, price) "documents": [ { "_id": 1, - "title": "Pride & Prejudice", + "title": null, "outOfStock": false, - "publishYear": 1813, + "publishYear": null, "isbn13": 9780141439518, - "discount": 0.2, + "discount": null, "price": {"$numberDecimal": "23.55"} }, { "_id": 2, "title": "War & Peace", - "outOfStock": false, + "outOfStock": null, "publishYear": 1867, - "isbn13": 9780143039990, + "isbn13": null, "discount": 0.1, - "price": {"$numberDecimal": "19.99"} + "price": null } ] } @@ -156,11 +156,11 @@ insert into Book (id, title, outOfStock, publishYear, isbn13, discount, price) """ { "_id": 1, - "title": "Pride & Prejudice", + "title": null, "outOfStock": false, - "publishYear": 1813, + "publishYear": null, "isbn13": 9780141439518, - "discount": 0.2, + "discount": null, "price": {"$numberDecimal": "23.55"} } """), @@ -169,11 +169,11 @@ insert into Book (id, title, outOfStock, publishYear, isbn13, discount, price) { "_id": 2, "title": "War & Peace", - "outOfStock": false, + "outOfStock": null, "publishYear": 1867, - "isbn13": 9780143039990, + "isbn13": null, "discount": 0.1, - "price": {"$numberDecimal": "19.99"} + "price": null } """)), Set.of(Book.COLLECTION_NAME)); diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/mutation/UpdatingIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/query/mutation/UpdatingIntegrationTests.java index 6edac076..8cf65b05 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/query/mutation/UpdatingIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/query/mutation/UpdatingIntegrationTests.java @@ -82,9 +82,9 @@ void testUpdateWithNonZeroMutationCount() { "title": "War and Peace", "outOfStock": false, "publishYear": 1869, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -94,9 +94,9 @@ void testUpdateWithNonZeroMutationCount() { "title": "Crime and Punishment", "outOfStock": false, "publishYear": 1866, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -106,9 +106,9 @@ void testUpdateWithNonZeroMutationCount() { "title": "Anna Karenina", "outOfStock": false, "publishYear": 1877, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -118,9 +118,9 @@ void testUpdateWithNonZeroMutationCount() { "title": "The Brothers Karamazov", "outOfStock": false, "publishYear": 1880, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -130,9 +130,9 @@ void testUpdateWithNonZeroMutationCount() { "title": "War and Peace", "outOfStock": false, "publishYear": 2025, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """)), Set.of(Book.COLLECTION_NAME)); @@ -173,9 +173,9 @@ void testUpdateWithZeroMutationCount() { "title": "War & Peace", "outOfStock": true, "publishYear": 1869, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -185,9 +185,9 @@ void testUpdateWithZeroMutationCount() { "title": "Crime and Punishment", "outOfStock": false, "publishYear": 1866, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -197,9 +197,9 @@ void testUpdateWithZeroMutationCount() { "title": "Anna Karenina", "outOfStock": false, "publishYear": 1877, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -209,9 +209,9 @@ void testUpdateWithZeroMutationCount() { "title": "The Brothers Karamazov", "outOfStock": false, "publishYear": 1880, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """), BsonDocument.parse( @@ -221,9 +221,9 @@ void testUpdateWithZeroMutationCount() { "title": "War & Peace", "outOfStock": false, "publishYear": 2025, - "isbn13": {"$numberLong": "0"}, - "discount": {"$numberDouble": "0"}, - "price": {"$numberDecimal": "0.0"} + "isbn13": null, + "discount": null, + "price": null } """)), Set.of(Book.COLLECTION_NAME)); diff --git a/src/integrationTest/java/com/mongodb/hibernate/type/ObjectIdIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/type/ObjectIdIntegrationTests.java index 2fd47d01..93e7b13b 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/type/ObjectIdIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/type/ObjectIdIntegrationTests.java @@ -32,6 +32,7 @@ import jakarta.persistence.Table; import org.bson.BsonDocument; import org.bson.BsonInt32; +import org.bson.BsonNull; import org.bson.BsonObjectId; import org.bson.types.ObjectId; import org.hibernate.annotations.JavaType; @@ -68,17 +69,14 @@ void insert() { var item = new Item(); item.id = 1; item.v = new ObjectId(1, 0); - // TODO-HIBERNATE-74 https://jira.mongodb.org/browse/HIBERNATE-74, - // TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 Make sure `item.vNull` is set to `null` - // when we implement `MongoPreparedStatement.setNull` properly. - item.vNull = new ObjectId(); + item.vNull = null; item.vExplicitlyAnnotatedNotForThePublic = new ObjectId(2, 3); sessionFactoryScope.inTransaction(session -> session.persist(item)); assertThat(mongoCollection.find()) .containsExactly(new BsonDocument() .append(ID_FIELD_NAME, new BsonInt32(1)) .append("v", new BsonObjectId(item.v)) - .append("vNull", new BsonObjectId(item.vNull)) + .append("vNull", BsonNull.VALUE) .append( "vExplicitlyAnnotatedNotForThePublic", new BsonObjectId(item.vExplicitlyAnnotatedNotForThePublic))); @@ -89,10 +87,7 @@ void findById() { var item = new Item(); item.id = 1; item.v = new ObjectId(2, 0); - // TODO-HIBERNATE-74 https://jira.mongodb.org/browse/HIBERNATE-74, - // TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 Make sure `item.vNull` is set to `null` - // when we implement `MongoPreparedStatement.setNull` properly. - item.vNull = new ObjectId(); + item.vNull = null; item.vExplicitlyAnnotatedNotForThePublic = new ObjectId(3, 4); sessionFactoryScope.inTransaction(session -> session.persist(item)); var loadedItem = sessionFactoryScope.fromTransaction(session -> session.find(Item.class, item.id)); diff --git a/src/main/java/com/mongodb/hibernate/internal/extension/MongoAdditionalMappingContributor.java b/src/main/java/com/mongodb/hibernate/internal/extension/MongoAdditionalMappingContributor.java index 0eb77722..3c217406 100644 --- a/src/main/java/com/mongodb/hibernate/internal/extension/MongoAdditionalMappingContributor.java +++ b/src/main/java/com/mongodb/hibernate/internal/extension/MongoAdditionalMappingContributor.java @@ -26,7 +26,6 @@ import jakarta.persistence.Embeddable; import java.util.Collection; import java.util.Set; -import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.Struct; import org.hibernate.boot.ResourceStreamLocator; import org.hibernate.boot.spi.AdditionalMappingContributions; @@ -59,20 +58,12 @@ public void contribute( MetadataBuildingContext buildingContext) { forbidEmbeddablesWithoutPersistentAttributes(metadata); metadata.getEntityBindings().forEach(persistentClass -> { - forbidDynamicInsert(persistentClass); checkColumnNames(persistentClass); forbidStructIdentifier(persistentClass); setIdentifierColumnName(persistentClass); }); } - private static void forbidDynamicInsert(PersistentClass persistentClass) { - if (persistentClass.useDynamicInsert()) { - throw new FeatureNotSupportedException( - format("%s: %s is not supported", persistentClass, DynamicInsert.class.getSimpleName())); - } - } - private static void checkColumnNames(PersistentClass persistentClass) { persistentClass .getTable() diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index b8493c5d..d1d04cb5 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -166,7 +166,6 @@ import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate; import org.hibernate.sql.ast.tree.predicate.ThruthnessPredicate; import org.hibernate.sql.ast.tree.select.QueryGroup; @@ -537,7 +536,7 @@ public void visitSelectClause(SelectClause selectClause) { var projectStageSpecifications = new ArrayList( selectClause.getSqlSelections().size()); - for (SqlSelection sqlSelection : selectClause.getSqlSelections()) { + for (var sqlSelection : selectClause.getSqlSelections()) { if (sqlSelection.isVirtual()) { continue; } @@ -561,16 +560,13 @@ public void visitColumnReference(ColumnReference columnReference) { @Override public void visitQueryLiteral(QueryLiteral queryLiteral) { var literalValue = queryLiteral.getLiteralValue(); - if (literalValue == null) { - throw new FeatureNotSupportedException("TODO-HIBERNATE-74 https://jira.mongodb.org/browse/HIBERNATE-74"); - } astVisitorValueHolder.yield(VALUE, new AstLiteralValue(toBsonValue(literalValue))); } @Override public void visitJunction(Junction junction) { var subFilters = new ArrayList(junction.getPredicates().size()); - for (Predicate predicate : junction.getPredicates()) { + for (var predicate : junction.getPredicates()) { subFilters.add(acceptAndYield(predicate, FILTER)); } var junctionFilter = @@ -1015,17 +1011,6 @@ static String renderMongoAstNode(AstNode rootAstNode) { } } - static void checkJdbcParameterBindingsSupportability(@Nullable JdbcParameterBindings jdbcParameterBindings) { - if (jdbcParameterBindings != null) { - for (var jdbcParameterBinding : jdbcParameterBindings.getBindings()) { - if (jdbcParameterBinding.getBindValue() == null) { - throw new FeatureNotSupportedException( - "TODO-HIBERNATE-74 https://jira.mongodb.org/browse/HIBERNATE-74"); - } - } - } - } - private static void checkQueryOptionsSupportability(QueryOptions queryOptions) { if (queryOptions.getTimeout() != null) { throw new FeatureNotSupportedException("'timeout' inQueryOptions is not supported"); @@ -1101,8 +1086,7 @@ private static boolean isComparingFieldWithValue(ComparisonPredicate comparisonP || (isFieldPathExpression(rhs) && isValueExpression(lhs)); } - private static BsonValue toBsonValue(Object value) { - // TODO-HIBERNATE-74 decide if `value` is nullable + private static BsonValue toBsonValue(@Nullable Object value) { try { return ValueConversions.toBsonValue(value); } catch (SQLFeatureNotSupportedException e) { diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/MutationMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/MutationMqlTranslator.java index 58de052e..edbe4e10 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/MutationMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/MutationMqlTranslator.java @@ -54,7 +54,6 @@ public JdbcOperationQueryMutation translate( logSqlAst(mutationStatement); - checkJdbcParameterBindingsSupportability(jdbcParameterBindings); applyQueryOptions(queryOptions); var result = acceptAndYield(mutationStatement, MUTATION_RESULT); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java index 2e6981b6..d35e9c0e 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java @@ -51,7 +51,6 @@ public JdbcOperationQuerySelect translate( logSqlAst(selectStatement); - checkJdbcParameterBindingsSupportability(jdbcParameterBindings); applyQueryOptions(queryOptions); var result = acceptAndYield((Statement) selectStatement, SELECT_RESULT); diff --git a/src/main/java/com/mongodb/hibernate/internal/type/MongoStructJdbcType.java b/src/main/java/com/mongodb/hibernate/internal/type/MongoStructJdbcType.java index 362403ca..c71587f9 100644 --- a/src/main/java/com/mongodb/hibernate/internal/type/MongoStructJdbcType.java +++ b/src/main/java/com/mongodb/hibernate/internal/type/MongoStructJdbcType.java @@ -101,23 +101,23 @@ public EmbeddableMappingType getEmbeddableMappingType() { } /** - * We replaced this method with {@link #createBsonValue(Object, WrapperOptions)}, to make it clear that this method + * We replaced this method with {@link #createBindValue(Object, WrapperOptions)}, to make it clear that this method * is not called by Hibernate ORM. */ @Override - public BsonValue createJdbcValue(@Nullable Object domainValue, WrapperOptions options) { + public BsonDocument createJdbcValue(@Nullable Object domainValue, WrapperOptions options) { throw fail(); } - private BsonValue createBsonValue(@Nullable Object domainValue, WrapperOptions options) throws SQLException { + private @Nullable BsonDocument createBindValue(@Nullable Object domainValue, WrapperOptions options) + throws SQLException { if (domainValue == null) { - throw new FeatureNotSupportedException( - "TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 return toBsonValue(domainValue)"); + return null; } var embeddableMappingType = getEmbeddableMappingType(); var result = new BsonDocument(); var jdbcValueCount = embeddableMappingType.getJdbcValueCount(); - for (int columnIndex = 0; columnIndex < jdbcValueCount; columnIndex++) { + for (var columnIndex = 0; columnIndex < jdbcValueCount; columnIndex++) { var jdbcValueSelectable = embeddableMappingType.getJdbcValueSelectable(columnIndex); assertFalse(jdbcValueSelectable.isFormula()); if (!jdbcValueSelectable.isInsertable()) { @@ -130,22 +130,22 @@ private BsonValue createBsonValue(@Nullable Object domainValue, WrapperOptions o } var fieldName = jdbcValueSelectable.getSelectableName(); var value = embeddableMappingType.getValue(domainValue, columnIndex); - if (value == null) { - throw new FeatureNotSupportedException( - "TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48"); - } BsonValue bsonValue; - var jdbcMapping = jdbcValueSelectable.getJdbcMapping(); - var jdbcTypeCode = jdbcMapping.getJdbcType().getJdbcTypeCode(); - if (jdbcTypeCode == getJdbcTypeCode()) { - var structValueBinder = assertInstanceOf(jdbcMapping.getJdbcValueBinder(), Binder.class); - bsonValue = structValueBinder.getJdbcType().createBsonValue(value, options); - } else if (jdbcTypeCode == MongoArrayJdbcType.JDBC_TYPE.getVendorTypeNumber()) { - @SuppressWarnings("unchecked") - ValueBinder valueBinder = jdbcMapping.getJdbcValueBinder(); - bsonValue = toBsonValue(valueBinder.getBindValue(value, options)); - } else { + if (value == null) { bsonValue = toBsonValue(value); + } else { + var jdbcMapping = jdbcValueSelectable.getJdbcMapping(); + var jdbcTypeCode = jdbcMapping.getJdbcType().getJdbcTypeCode(); + if (jdbcTypeCode == getJdbcTypeCode()) { + var structValueBinder = assertInstanceOf(jdbcMapping.getJdbcValueBinder(), Binder.class); + bsonValue = structValueBinder.getJdbcType().createBindValue(value, options); + } else if (jdbcTypeCode == MongoArrayJdbcType.JDBC_TYPE.getVendorTypeNumber()) { + @SuppressWarnings("unchecked") + ValueBinder valueBinder = jdbcMapping.getJdbcValueBinder(); + bsonValue = toBsonValue(valueBinder.getBindValue(value, options)); + } else { + bsonValue = toBsonValue(value); + } } result.append(fieldName, bsonValue); } @@ -162,14 +162,13 @@ private BsonValue createBsonValue(@Nullable Object domainValue, WrapperOptions o public Object @Nullable [] extractJdbcValues(@Nullable Object rawJdbcValue, WrapperOptions options) throws SQLException { if (isNull(rawJdbcValue)) { - throw new FeatureNotSupportedException( - "TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 return null"); + return null; } var bsonDocument = assertInstanceOf(rawJdbcValue, BsonDocument.class); var embeddableMappingType = getEmbeddableMappingType(); var jdbcValueCount = embeddableMappingType.getJdbcValueCount(); var result = new Object[jdbcValueCount]; - for (int columnIndex = 0; columnIndex < jdbcValueCount; columnIndex++) { + for (var columnIndex = 0; columnIndex < jdbcValueCount; columnIndex++) { var jdbcValueSelectable = embeddableMappingType.getJdbcValueSelectable(columnIndex); assertFalse(jdbcValueSelectable.isFormula()); var fieldName = jdbcValueSelectable.getSelectableName(); @@ -178,8 +177,7 @@ private BsonValue createBsonValue(@Nullable Object domainValue, WrapperOptions o var jdbcTypeCode = jdbcMapping.getJdbcType().getJdbcTypeCode(); Object domainValue; if (isNull(value)) { - throw new FeatureNotSupportedException( - "TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 domainValue = null"); + domainValue = null; } else if (jdbcTypeCode == getJdbcTypeCode()) { var structValueExtractor = assertInstanceOf(jdbcMapping.getJdbcValueExtractor(), Extractor.class); domainValue = structValueExtractor.getJdbcType().extractJdbcValues(value, options); @@ -223,8 +221,8 @@ public MongoStructJdbcType getJdbcType() { } @Override - public Object getBindValue(@Nullable X value, WrapperOptions options) throws SQLException { - return getJdbcType().createBsonValue(value, options); + public @Nullable Object getBindValue(@Nullable X value, WrapperOptions options) throws SQLException { + return getJdbcType().createBindValue(value, options); } @Override diff --git a/src/main/java/com/mongodb/hibernate/internal/type/ObjectIdJavaType.java b/src/main/java/com/mongodb/hibernate/internal/type/ObjectIdJavaType.java index 8994c2ca..9b693201 100644 --- a/src/main/java/com/mongodb/hibernate/internal/type/ObjectIdJavaType.java +++ b/src/main/java/com/mongodb/hibernate/internal/type/ObjectIdJavaType.java @@ -58,7 +58,9 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { @Override public @Nullable ObjectId wrap(@Nullable X value, WrapperOptions options) { - if (value instanceof ObjectId v) { + if (value == null) { + return null; + } else if (value instanceof ObjectId v) { return v; } else if (value instanceof BsonValue v) { return toObjectIdDomainValue(v); diff --git a/src/main/java/com/mongodb/hibernate/internal/type/ValueConversions.java b/src/main/java/com/mongodb/hibernate/internal/type/ValueConversions.java index 128d09c4..9fed2abd 100644 --- a/src/main/java/com/mongodb/hibernate/internal/type/ValueConversions.java +++ b/src/main/java/com/mongodb/hibernate/internal/type/ValueConversions.java @@ -56,8 +56,7 @@ private ValueConversions() {} public static BsonValue toBsonValue(@Nullable Object value) throws SQLFeatureNotSupportedException { if (value == null) { - throw new SQLFeatureNotSupportedException( - "TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 return BsonNull.VALUE"); + return BsonNull.VALUE; } else if (value instanceof BsonDocument v) { return v; } else if (value instanceof Boolean v) { @@ -85,7 +84,7 @@ public static BsonValue toBsonValue(@Nullable Object value) throws SQLFeatureNot } throw new SQLFeatureNotSupportedException(format( "Value [%s] of type [%s] is not supported", - value, value.getClass().getTypeName())); + value, assertNotNull(value).getClass().getTypeName())); } public static BsonBoolean toBsonValue(boolean value) { @@ -169,10 +168,9 @@ private static BsonArray arrayToBsonValue(Object value) throws SQLFeatureNotSupp return new BsonArray(elements); } - static Object toDomainValue(BsonValue value, Class domainType) throws SQLFeatureNotSupportedException { + static @Nullable Object toDomainValue(BsonValue value, Class domainType) throws SQLFeatureNotSupportedException { if (isNull(value)) { - throw new SQLFeatureNotSupportedException( - "TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 return null"); + return null; } else if (value instanceof BsonDocument v) { return v; } else if (value instanceof BsonBoolean v) { diff --git a/src/main/java/com/mongodb/hibernate/jdbc/MongoPreparedStatement.java b/src/main/java/com/mongodb/hibernate/jdbc/MongoPreparedStatement.java index f693ae17..e21e8a7c 100644 --- a/src/main/java/com/mongodb/hibernate/jdbc/MongoPreparedStatement.java +++ b/src/main/java/com/mongodb/hibernate/jdbc/MongoPreparedStatement.java @@ -23,6 +23,7 @@ import com.mongodb.client.ClientSession; import com.mongodb.client.MongoDatabase; +import com.mongodb.hibernate.internal.FeatureNotSupportedException; import com.mongodb.hibernate.internal.type.MongoStructJdbcType; import com.mongodb.hibernate.internal.type.ObjectIdJdbcType; import java.math.BigDecimal; @@ -40,6 +41,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.List; +import java.util.Set; import java.util.function.Consumer; import org.bson.BsonArray; import org.bson.BsonDocument; @@ -84,6 +86,7 @@ private void checkAllParametersSet() throws SQLException { throw new SQLException(format("Parameter with index [%d] is not set", i + 1)); } } + checkComparatorNotComparingWithNullValues(command); } @Override @@ -91,7 +94,6 @@ public void setNull(int parameterIndex, int sqlType) throws SQLException { checkClosed(); checkParameterIndex(parameterIndex); switch (sqlType) { - case Types.ARRAY: case Types.BLOB: case Types.CLOB: case Types.DATALINK: @@ -103,13 +105,10 @@ public void setNull(int parameterIndex, int sqlType) throws SQLException { case Types.REF: case Types.ROWID: case Types.SQLXML: - case Types.STRUCT: throw new SQLFeatureNotSupportedException( "Unsupported SQL type: " + JDBCType.valueOf(sqlType).getName()); } - throw new SQLFeatureNotSupportedException( - "TODO-HIBERNATE-74 https://jira.mongodb.org/browse/HIBERNATE-74, TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48" - + " setParameter(parameterIndex, ValueConversions.toBsonValue((Object) null))"); + setParameter(parameterIndex, toBsonValue((Object) null)); } @Override @@ -293,10 +292,10 @@ private static void parseParameters(BsonArray array, List } private static void parseParameters(BsonValue value, List parameterValueSetters) { - if (value.isDocument()) { - parseParameters(value.asDocument(), parameterValueSetters); - } else if (value.isArray()) { - parseParameters(value.asArray(), parameterValueSetters); + if (value instanceof BsonDocument document) { + parseParameters(document, parameterValueSetters); + } else if (value instanceof BsonArray array) { + parseParameters(array, parameterValueSetters); } else { fail("Only BSON container type (BsonDocument or BsonArray) is accepted; provided type: " + value.getBsonType()); @@ -336,4 +335,30 @@ boolean isUsed() { return used; } } + + /** + * Temporary method to ensure exception is thrown when comparison query operators are comparing with {@code null} + * values. + * + *

Note that only find expression is involved before HIBERNATE-74. TODO-HIBERNATE-74 delete this temporary method + */ + private static void checkComparatorNotComparingWithNullValues(BsonDocument document) { + var comparisonOperators = Set.of("$eq", "$ne", "$gt", "$gte", "$lt", "$lte", "$in", "$nin"); + for (var entry : document.entrySet()) { + var value = entry.getValue(); + if (value.isNull() && comparisonOperators.contains(entry.getKey())) { + throw new FeatureNotSupportedException( + "TODO-HIBERNATE-74 https://jira.mongodb.org/browse/HIBERNATE-74"); + } + if (value instanceof BsonDocument documentValue) { + checkComparatorNotComparingWithNullValues(documentValue); + } else if (value instanceof BsonArray arrayValue) { + for (var element : arrayValue) { + if (element instanceof BsonDocument documentElement) { + checkComparatorNotComparingWithNullValues(documentElement); + } + } + } + } + } } diff --git a/src/main/java/com/mongodb/hibernate/jdbc/MongoResultSet.java b/src/main/java/com/mongodb/hibernate/jdbc/MongoResultSet.java index 12d3f47f..7df76973 100644 --- a/src/main/java/com/mongodb/hibernate/jdbc/MongoResultSet.java +++ b/src/main/java/com/mongodb/hibernate/jdbc/MongoResultSet.java @@ -37,7 +37,6 @@ import static java.lang.String.format; import com.mongodb.client.MongoCursor; -import com.mongodb.hibernate.internal.FeatureNotSupportedException; import com.mongodb.hibernate.internal.type.ValueConversions; import java.math.BigDecimal; import java.sql.Array; @@ -252,14 +251,6 @@ private T getValue(int columnIndex, SqlFunction toJavaConverte try { var key = getKey(columnIndex); var bsonValue = assertNotNull(currentDocument).get(key); - if (bsonValue == null) { - throw new FeatureNotSupportedException( - """ - TODO-HIBERNATE-48 https://jira.mongodb.org/browse/HIBERNATE-48 - simply remove this `null` check, as we support missing fields - and they are equivalent to BSON `Null`, which is handled below - """); - } T value = ValueConversions.isNull(bsonValue) ? null : toJavaConverter.apply(assertNotNull(bsonValue)); lastReadColumnValueWasNull = value == null; return value;