Skip to content

Commit 1d87db4

Browse files
imunicbeikov
authored andcommitted
HHH-17840 Fix inconsistency of read/write null JsonNode/JsonValue
1 parent c972208 commit 1d87db4

File tree

17 files changed

+235
-79
lines changed

17 files changed

+235
-79
lines changed

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
7070
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
7171
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
72-
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
7372
import org.hibernate.type.descriptor.jdbc.JdbcType;
7473
import org.hibernate.type.descriptor.jdbc.TimeAsTimestampWithTimeZoneJdbcType;
7574
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
@@ -287,7 +286,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
287286
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
288287
}
289288
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
290-
jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE );
289+
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
291290
}
292291
}
293292

hibernate-core/hibernate-core.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ dependencies {
7070
testRuntimeOnly libs.byteBuddy
7171
testRuntimeOnly testLibs.weld
7272
testRuntimeOnly testLibs.wildFlyTxnClient
73-
testRuntimeOnly jakartaLibs.jsonb
74-
testRuntimeOnly libs.jackson
73+
testImplementation jakartaLibs.jsonb
74+
testImplementation libs.jackson
7575
testRuntimeOnly libs.jacksonXml
7676
testRuntimeOnly libs.jacksonJsr310
7777

hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation;
6767
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
6868
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
69-
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
7069
import org.hibernate.type.descriptor.jdbc.JdbcType;
7170
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
7271
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType;
@@ -235,7 +234,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
235234
jdbcTypeRegistry.addDescriptor( TimestampUtcAsInstantJdbcType.INSTANCE );
236235
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
237236
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
238-
jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE );
237+
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
239238
}
240239

241240
@Override
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
5+
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
6+
*/
7+
package org.hibernate.dialect;
8+
9+
import java.nio.charset.StandardCharsets;
10+
import java.sql.CallableStatement;
11+
import java.sql.PreparedStatement;
12+
import java.sql.SQLException;
13+
14+
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
15+
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
16+
import org.hibernate.type.descriptor.ValueBinder;
17+
import org.hibernate.type.descriptor.WrapperOptions;
18+
import org.hibernate.type.descriptor.java.JavaType;
19+
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
20+
import org.hibernate.type.descriptor.jdbc.BasicBinder;
21+
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
22+
23+
/**
24+
* H2 requires binding JSON via {@code setBytes} methods.
25+
*/
26+
public class H2JsonJdbcType extends JsonJdbcType {
27+
/**
28+
* Singleton access
29+
*/
30+
public static final H2JsonJdbcType INSTANCE = new H2JsonJdbcType( null );
31+
32+
protected H2JsonJdbcType(EmbeddableMappingType embeddableMappingType) {
33+
super( embeddableMappingType );
34+
}
35+
36+
@Override
37+
public String toString() {
38+
return "FormatJsonJdbcType";
39+
}
40+
41+
@Override
42+
public AggregateJdbcType resolveAggregateJdbcType(
43+
EmbeddableMappingType mappingType,
44+
String sqlType,
45+
RuntimeModelCreationContext creationContext) {
46+
return new H2JsonJdbcType( mappingType );
47+
}
48+
49+
@Override
50+
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
51+
return new BasicBinder<>( javaType, this ) {
52+
@Override
53+
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
54+
throws SQLException {
55+
final String json = ( (H2JsonJdbcType) getJdbcType() ).toString( value, getJavaType(), options );
56+
st.setBytes( index, json.getBytes( StandardCharsets.UTF_8 ) );
57+
}
58+
59+
@Override
60+
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
61+
throws SQLException {
62+
final String json = ( (H2JsonJdbcType) getJdbcType() ).toString( value, getJavaType(), options );
63+
st.setBytes( name, json.getBytes( StandardCharsets.UTF_8 ) );
64+
}
65+
};
66+
}
67+
}

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.hibernate.query.sqm.tree.SqmCopyContext;
1414
import org.hibernate.type.descriptor.java.JavaType;
1515

16+
import org.checkerframework.checker.nullness.qual.Nullable;
17+
1618
/**
1719
* Represents a literal value in the sqm, e.g.<ul>
1820
* <li>1</li>
@@ -75,13 +77,18 @@ public void appendHqlString(StringBuilder sb) {
7577
appendHqlString( sb, getJavaTypeDescriptor(), getLiteralValue() );
7678
}
7779

78-
public static <T> void appendHqlString(StringBuilder sb, JavaType<T> javaType, T value) {
79-
final String string = javaType.toString( value );
80-
if ( javaType.getJavaTypeClass() == String.class ) {
81-
QueryLiteralHelper.appendStringLiteral( sb, string );
80+
public static <T> void appendHqlString(StringBuilder sb, JavaType<T> javaType, @Nullable T value) {
81+
if ( value == null ) {
82+
sb.append( "null" );
8283
}
8384
else {
84-
sb.append( string );
85+
final String string = javaType.toString( value );
86+
if ( javaType.getJavaTypeClass() == String.class ) {
87+
QueryLiteralHelper.appendStringLiteral( sb, string );
88+
}
89+
else {
90+
sb.append( string );
91+
}
8592
}
8693
}
8794

hibernate-core/src/main/java/org/hibernate/type/EnumType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ public T fromStringValue(CharSequence sequence) {
290290
@Override @SuppressWarnings("unchecked")
291291
public String toLoggableString(Object value, SessionFactoryImplementor factory) {
292292
verifyConfigured();
293-
return enumJavaType.toString( (T) value );
293+
return enumJavaType.extractLoggableRepresentation( (T) value );
294294
}
295295

296296
public boolean isOrdinal() {

hibernate-core/src/main/java/org/hibernate/type/Type.java

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public interface Type extends Serializable {
143143
*
144144
* @throws HibernateException A problem occurred performing the comparison
145145
*/
146-
boolean isSame(Object x, Object y) throws HibernateException;
146+
boolean isSame(@Nullable Object x, @Nullable Object y) throws HibernateException;
147147

148148
/**
149149
* Compare two instances of the class mapped by this type for persistence "equality",
@@ -162,7 +162,7 @@ public interface Type extends Serializable {
162162
*
163163
* @throws HibernateException A problem occurred performing the comparison
164164
*/
165-
boolean isEqual(Object x, Object y) throws HibernateException;
165+
boolean isEqual(@Nullable Object x, @Nullable Object y) throws HibernateException;
166166

167167
/**
168168
* Compare two instances of the class mapped by this type for persistence "equality",
@@ -182,7 +182,7 @@ public interface Type extends Serializable {
182182
*
183183
* @throws HibernateException A problem occurred performing the comparison
184184
*/
185-
boolean isEqual(Object x, Object y, SessionFactoryImplementor factory) throws HibernateException;
185+
boolean isEqual(@Nullable Object x, @Nullable Object y, SessionFactoryImplementor factory) throws HibernateException;
186186

187187
/**
188188
* Get a hash code, consistent with persistence "equality". For most types this could
@@ -218,9 +218,9 @@ public interface Type extends Serializable {
218218
*
219219
* @see java.util.Comparator#compare(Object, Object)
220220
*/
221-
int compare(Object x, Object y);
221+
int compare(@Nullable Object x, @Nullable Object y);
222222

223-
int compare(Object x, Object y, SessionFactoryImplementor sessionFactory);
223+
int compare(@Nullable Object x, @Nullable Object y, SessionFactoryImplementor sessionFactory);
224224

225225
/**
226226
* Should the parent be considered dirty, given both the old and current value?
@@ -233,7 +233,7 @@ public interface Type extends Serializable {
233233
*
234234
* @throws HibernateException A problem occurred performing the checking
235235
*/
236-
boolean isDirty(Object old, Object current, SharedSessionContractImplementor session) throws HibernateException;
236+
boolean isDirty(@Nullable Object old, @Nullable Object current, SharedSessionContractImplementor session) throws HibernateException;
237237

238238
/**
239239
* Should the parent be considered dirty, given both the old and current value?
@@ -247,7 +247,7 @@ public interface Type extends Serializable {
247247
*
248248
* @throws HibernateException A problem occurred performing the checking
249249
*/
250-
boolean isDirty(Object oldState, Object currentState, boolean[] checkable, SharedSessionContractImplementor session)
250+
boolean isDirty(@Nullable Object oldState, @Nullable Object currentState, boolean[] checkable, SharedSessionContractImplementor session)
251251
throws HibernateException;
252252

253253
/**
@@ -266,8 +266,8 @@ boolean isDirty(Object oldState, Object currentState, boolean[] checkable, Share
266266
* @throws HibernateException A problem occurred performing the checking
267267
*/
268268
boolean isModified(
269-
Object dbState,
270-
Object currentState,
269+
@Nullable Object dbState,
270+
@Nullable Object currentState,
271271
boolean[] checkable,
272272
SharedSessionContractImplementor session)
273273
throws HibernateException;
@@ -289,7 +289,7 @@ boolean isModified(
289289
*/
290290
void nullSafeSet(
291291
PreparedStatement st,
292-
Object value,
292+
@Nullable Object value,
293293
int index,
294294
boolean[] settable,
295295
SharedSessionContractImplementor session)
@@ -309,7 +309,7 @@ void nullSafeSet(
309309
* @throws HibernateException An error from Hibernate
310310
* @throws SQLException An error from the JDBC driver
311311
*/
312-
void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
312+
void nullSafeSet(PreparedStatement st, @Nullable Object value, int index, SharedSessionContractImplementor session)
313313
throws HibernateException, SQLException;
314314

315315
/**
@@ -342,7 +342,7 @@ String toLoggableString(@Nullable Object value, SessionFactoryImplementor factor
342342
*
343343
* @throws HibernateException An error from Hibernate
344344
*/
345-
Object deepCopy(Object value, SessionFactoryImplementor factory)
345+
@Nullable Object deepCopy(@Nullable Object value, SessionFactoryImplementor factory)
346346
throws HibernateException;
347347

348348
/**
@@ -381,7 +381,7 @@ Object deepCopy(Object value, SessionFactoryImplementor factory)
381381
*
382382
* @throws HibernateException An error from Hibernate
383383
*/
384-
default Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) throws HibernateException {
384+
default @Nullable Serializable disassemble(@Nullable Object value, SessionFactoryImplementor sessionFactory) throws HibernateException {
385385
return disassemble( value, null, null );
386386
}
387387

@@ -399,7 +399,7 @@ default Serializable disassemble(Object value, SessionFactoryImplementor session
399399
*
400400
* @throws HibernateException An error from Hibernate
401401
*/
402-
Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner) throws HibernateException;
402+
@Nullable Serializable disassemble(@Nullable Object value, @Nullable SharedSessionContractImplementor session, @Nullable Object owner) throws HibernateException;
403403

404404
/**
405405
* Reconstruct the object from its disassembled state. This function is the inverse of
@@ -413,15 +413,17 @@ default Serializable disassemble(Object value, SessionFactoryImplementor session
413413
*
414414
* @throws HibernateException An error from Hibernate
415415
*/
416-
Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException;
416+
@Nullable Object assemble(@Nullable Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException;
417417

418418
/**
419419
* Called before assembling a query result set from the query cache, to allow batch
420420
* fetching of entities missing from the second-level cache.
421421
*
422422
* @param cached The key
423423
* @param session The originating session
424+
* @deprecated Is not called anymore
424425
*/
426+
@Deprecated(forRemoval = true, since = "6.6")
425427
void beforeAssemble(Serializable cached, SharedSessionContractImplementor session);
426428

427429
/**
@@ -441,9 +443,9 @@ default Serializable disassemble(Object value, SessionFactoryImplementor session
441443
*
442444
* @throws HibernateException An error from Hibernate
443445
*/
444-
Object replace(
445-
Object original,
446-
Object target,
446+
@Nullable Object replace(
447+
@Nullable Object original,
448+
@Nullable Object target,
447449
SharedSessionContractImplementor session,
448450
Object owner,
449451
Map<Object, Object> copyCache) throws HibernateException;
@@ -466,9 +468,9 @@ Object replace(
466468
*
467469
* @throws HibernateException An error from Hibernate
468470
*/
469-
Object replace(
470-
Object original,
471-
Object target,
471+
@Nullable Object replace(
472+
@Nullable Object original,
473+
@Nullable Object target,
472474
SharedSessionContractImplementor session,
473475
Object owner,
474476
Map<Object, Object> copyCache,
@@ -483,5 +485,5 @@ Object replace(
483485
*
484486
* @return array indicating column nullness for a value instance
485487
*/
486-
boolean[] toColumnNullness(Object value, Mapping mapping);
488+
boolean[] toColumnNullness(@Nullable Object value, Mapping mapping);
487489
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
2727
import org.hibernate.type.spi.TypeConfiguration;
2828

29+
import org.checkerframework.checker.nullness.qual.Nullable;
30+
2931
/**
3032
* Descriptor for the Java side of a value mapping. A {@code JavaType} is always
3133
* coupled with a {@link JdbcType} to describe the typing aspects of an attribute
@@ -228,12 +230,12 @@ default boolean areEqual(T one, T another) {
228230
*
229231
* @return The loggable representation
230232
*/
231-
default String extractLoggableRepresentation(T value) {
232-
return toString( value );
233+
default String extractLoggableRepresentation(@Nullable T value) {
234+
return value == null ? "null" : toString( value );
233235
}
234236

235237
default String toString(T value) {
236-
return value == null ? "null" : value.toString();
238+
return value.toString();
237239
}
238240

239241
T fromString(CharSequence string);

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutabilityPlan.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
import org.hibernate.SharedSessionContract;
1212

13+
import org.checkerframework.checker.nullness.qual.Nullable;
14+
1315
/**
1416
* Describes the mutability aspects of a given Java type.
1517
* <p>
@@ -65,7 +67,7 @@ public interface MutabilityPlan<T> extends Serializable {
6567
*
6668
* @return The deep copy.
6769
*/
68-
T deepCopy(T value);
70+
@Nullable T deepCopy(@Nullable T value);
6971

7072
/**
7173
* Return a disassembled representation of the value.
@@ -76,7 +78,7 @@ public interface MutabilityPlan<T> extends Serializable {
7678
*
7779
* @see #assemble
7880
*/
79-
Serializable disassemble(T value, SharedSessionContract session);
81+
@Nullable Serializable disassemble(@Nullable T value, SharedSessionContract session);
8082

8183
/**
8284
* Assemble a previously {@linkplain #disassemble disassembled} value.
@@ -87,5 +89,5 @@ public interface MutabilityPlan<T> extends Serializable {
8789
*
8890
* @see #disassemble
8991
*/
90-
T assemble(Serializable cached, SharedSessionContract session);
92+
@Nullable T assemble(@Nullable Serializable cached, SharedSessionContract session);
9193
}

0 commit comments

Comments
 (0)