Skip to content

Commit 0ca27a4

Browse files
committed
HHH-17246 Handle UUID specially within JSON
1 parent 532d546 commit 0ca27a4

File tree

9 files changed

+69
-68
lines changed

9 files changed

+69
-68
lines changed

Diff for: hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java

+7
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,13 @@ private static Object fromString(
12321232
),
12331233
options
12341234
);
1235+
case SqlTypes.UUID:
1236+
return jdbcJavaType.wrap(
1237+
PrimitiveByteArrayJavaType.INSTANCE.fromString(
1238+
string.substring( start, end ).replace( "-", "" )
1239+
),
1240+
options
1241+
);
12351242
case SqlTypes.DATE:
12361243
return jdbcJavaType.wrap(
12371244
JdbcDateJavaType.INSTANCE.fromEncodedString(

Diff for: hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java

-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@
8282
import org.hibernate.type.JavaObjectType;
8383
import org.hibernate.type.NullType;
8484
import org.hibernate.type.StandardBasicTypes;
85-
import org.hibernate.type.descriptor.java.OracleUUIDJavaType;
8685
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
8786
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
8887
import org.hibernate.type.descriptor.jdbc.JdbcType;
@@ -1042,8 +1041,6 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
10421041
)
10431042
);
10441043

1045-
typeContributions.contributeJavaType( OracleUUIDJavaType.INSTANCE );
1046-
10471044
if(getVersion().isSameOrAfter(23)) {
10481045
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
10491046
jdbcTypeRegistry.addDescriptor(OracleEnumJdbcType.INSTANCE);

Diff for: hibernate-core/src/main/java/org/hibernate/dialect/OracleUserDefinedTypeExporter.java

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
2929
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
3030
import static org.hibernate.type.SqlTypes.TINYINT;
31+
import static org.hibernate.type.SqlTypes.UUID;
3132
import static org.hibernate.type.SqlTypes.VARBINARY;
3233

3334
/**
@@ -355,6 +356,8 @@ private String determineValueExpression(String expression, int elementSqlTypeCod
355356
case VARBINARY:
356357
case LONG32VARBINARY:
357358
return "hextoraw(" + expression + ")";
359+
case UUID:
360+
return "hextoraw(replace(" + expression + ",'-',''))";
358361
default:
359362
return expression;
360363
}

Diff for: hibernate-core/src/main/java/org/hibernate/dialect/aggregate/DB2AggregateSupport.java

+23-6
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,15 @@
5050
import static org.hibernate.type.SqlTypes.TIMESTAMP;
5151
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
5252
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
53+
import static org.hibernate.type.SqlTypes.UUID;
5354
import static org.hibernate.type.SqlTypes.VARBINARY;
5455

5556
public class DB2AggregateSupport extends AggregateSupportImpl {
5657

5758
public static final AggregateSupport INSTANCE = new DB2AggregateSupport( false );
5859
public static final AggregateSupport JSON_INSTANCE = new DB2AggregateSupport( true );
60+
private static final String JSON_QUERY_START = "json_query(";
61+
private static final String JSON_QUERY_JSON_END = "')";
5962

6063
private final boolean jsonSupport;
6164

@@ -77,25 +80,32 @@ public String aggregateComponentCustomReadExpression(
7780
if ( !jsonSupport ) {
7881
break;
7982
}
83+
final String parentPartExpression;
84+
if ( aggregateParentReadExpression.startsWith( JSON_QUERY_START ) && aggregateParentReadExpression.endsWith( JSON_QUERY_JSON_END ) ) {
85+
parentPartExpression = aggregateParentReadExpression.substring( JSON_QUERY_START.length(), aggregateParentReadExpression.length() - JSON_QUERY_JSON_END.length() ) + ".";
86+
}
87+
else {
88+
parentPartExpression = aggregateParentReadExpression + ",'$.";
89+
}
8090
switch ( column.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) {
8191
case BOOLEAN:
8292
if ( SqlTypes.isNumericType( column.getJdbcMapping().getJdbcType().getDdlTypeCode() ) ) {
8393
return template.replace(
8494
placeholder,
85-
"decode(json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "'),'true',1,'false',0)"
95+
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0)"
8696
);
8797
}
8898
else {
8999
return template.replace(
90100
placeholder,
91-
"decode(json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "'),'true',true,'false',false)"
101+
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',true,'false',false)"
92102
);
93103
}
94104
case TIMESTAMP_WITH_TIMEZONE:
95105
case TIMESTAMP_UTC:
96106
return template.replace(
97107
placeholder,
98-
"cast(trim(trailing 'Z' from json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "' returning varchar(35))) as " + column.getColumnDefinition() + ")"
108+
"cast(trim(trailing 'Z' from json_value(" + parentPartExpression + columnExpression + "' returning varchar(35))) as " + column.getColumnDefinition() + ")"
99109
);
100110
case BINARY:
101111
case VARBINARY:
@@ -104,18 +114,23 @@ public String aggregateComponentCustomReadExpression(
104114
// We encode binary data as hex, so we have to decode here
105115
return template.replace(
106116
placeholder,
107-
"hextoraw(json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "'))"
117+
"hextoraw(json_value(" + parentPartExpression + columnExpression + "'))"
118+
);
119+
case UUID:
120+
return template.replace(
121+
placeholder,
122+
"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
108123
);
109124
case JSON:
110125
case JSON_ARRAY:
111126
return template.replace(
112127
placeholder,
113-
"json_query(" + aggregateParentReadExpression + ",'$." + columnExpression + "')"
128+
"json_query(" + parentPartExpression + columnExpression + "')"
114129
);
115130
default:
116131
return template.replace(
117132
placeholder,
118-
"json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "' returning " + column.getColumnDefinition() + ")"
133+
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ")"
119134
);
120135
}
121136
case STRUCT:
@@ -133,6 +148,8 @@ private static String jsonCustomWriteExpression(String customWriteExpression, Jd
133148
case BLOB:
134149
// We encode binary data as hex
135150
return "hex(" + customWriteExpression + ")";
151+
case UUID:
152+
return "regexp_replace(lower(hex(" + customWriteExpression + ")),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','$1-$2-$3-$4-$5')";
136153
case ARRAY:
137154
case JSON_ARRAY:
138155
return "(" + customWriteExpression + ") format json";

Diff for: hibernate-core/src/main/java/org/hibernate/dialect/aggregate/MySQLAggregateSupport.java

+14-5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import static org.hibernate.type.SqlTypes.TIMESTAMP;
4343
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
4444
import static org.hibernate.type.SqlTypes.TINYINT;
45+
import static org.hibernate.type.SqlTypes.UUID;
4546
import static org.hibernate.type.SqlTypes.VARBINARY;
4647
import static org.hibernate.type.SqlTypes.VARCHAR;
4748

@@ -89,6 +90,14 @@ public String aggregateComponentCustomReadExpression(
8990
placeholder,
9091
"unhex(json_unquote(" + queryExpression( aggregateParentReadExpression, columnExpression ) + "))"
9192
);
93+
case UUID:
94+
if ( column.getJdbcMapping().getJdbcType().isBinary() ) {
95+
return template.replace(
96+
placeholder,
97+
"unhex(replace(json_unquote(" + queryExpression( aggregateParentReadExpression, columnExpression ) + "),'-',''))"
98+
);
99+
}
100+
// Fall-through intended
92101
default:
93102
return template.replace(
94103
placeholder,
@@ -148,16 +157,16 @@ private static String jsonCustomWriteExpression(String customWriteExpression, Jd
148157
return "date_format(" + customWriteExpression + ",'%Y-%m-%dT%T.%f')";
149158
case TIMESTAMP_UTC:
150159
return "date_format(" + customWriteExpression + ",'%Y-%m-%dT%T.%fZ')";
160+
case UUID:
161+
if ( jdbcMapping.getJdbcType().isBinary() ) {
162+
return "regexp_replace(lower(hex(" + customWriteExpression + ")),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','$1-$2-$3-$4-$5')";
163+
}
164+
// Fall-through intended
151165
default:
152166
return customWriteExpression;
153167
}
154168
}
155169

156-
@Override
157-
public int aggregateComponentSqlTypeCode(int aggregateColumnSqlTypeCode, int columnSqlTypeCode) {
158-
return super.aggregateComponentSqlTypeCode( aggregateColumnSqlTypeCode, columnSqlTypeCode );
159-
}
160-
161170
@Override
162171
public String aggregateComponentAssignmentExpression(
163172
String aggregateParentAssignmentExpression,

Diff for: hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java

+9
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ public String aggregateComponentCustomReadExpression(
189189
placeholder,
190190
"hextoraw(json_value(" + parentPartExpression + columnExpression + "'))"
191191
);
192+
case UUID:
193+
return template.replace(
194+
placeholder,
195+
"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
196+
);
192197
case CLOB:
193198
case NCLOB:
194199
case BLOB:
@@ -277,12 +282,16 @@ private String jsonCustomWriteExpression(
277282
switch ( sqlTypeCode ) {
278283
case CLOB:
279284
return "to_clob(" + customWriteExpression + ")";
285+
case UUID:
286+
return "regexp_replace(lower(rawtohex(" + customWriteExpression + ")),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','\\1-\\2-\\3-\\4-\\5')";
280287
case ARRAY:
281288
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) jdbcMapping;
282289
final OracleArrayJdbcType jdbcType = (OracleArrayJdbcType) pluralType.getJdbcType();
283290
switch ( jdbcType.getElementJdbcType().getDefaultSqlTypeCode() ) {
284291
case CLOB:
285292
return "(select json_arrayagg(to_clob(t.column_value)) from table(" + customWriteExpression + ") t)";
293+
case UUID:
294+
return "(select json_arrayagg(regexp_replace(lower(rawtohex(t.column_value)),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','\\1-\\2-\\3-\\4-\\5')) from table(" + customWriteExpression + ") t)";
286295
case BIT:
287296
return "decode(" + customWriteExpression + ",1,'true',0,'false',null)";
288297
case BOOLEAN:

Diff for: hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OracleUUIDJavaType.java

-27
This file was deleted.

Diff for: hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/SybaseASEUUIDTest.java

+13-9
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44
*/
55
package org.hibernate.orm.test.id.uuid;
66

7+
import jakarta.persistence.Column;
78
import jakarta.persistence.Entity;
89
import jakarta.persistence.Id;
9-
import org.hibernate.annotations.JdbcType;
10+
import jakarta.persistence.Table;
1011
import org.hibernate.dialect.SybaseASEDialect;
1112
import org.hibernate.testing.orm.junit.DomainModel;
1213
import org.hibernate.testing.orm.junit.JiraKey;
1314
import org.hibernate.testing.orm.junit.RequiresDialect;
1415
import org.hibernate.testing.orm.junit.SessionFactory;
1516
import org.hibernate.testing.orm.junit.SessionFactoryScope;
17+
import org.hibernate.type.descriptor.java.UUIDJavaType;
1618
import org.junit.jupiter.api.AfterEach;
1719
import org.junit.jupiter.api.BeforeEach;
1820
import org.junit.jupiter.api.Test;
@@ -29,13 +31,15 @@
2931
@SessionFactory
3032
public class SybaseASEUUIDTest {
3133

32-
private static final UUID uuid = UUID.fromString("53886a8a-7082-4879-b430-25cb94415b00");
34+
private static final UUID THE_UUID = UUID.fromString("53886a8a-7082-4879-b430-25cb94415b00");
3335

3436
@BeforeEach
3537
void setUp(SessionFactoryScope scope) {
3638
scope.inTransaction( session -> {
37-
final Book book = new Book(uuid, "John Doe");
38-
session.persist( book );
39+
session.createNativeQuery( "insert into book (id, author) values (?,?)" )
40+
.setParameter( 1, UUIDJavaType.ToBytesTransformer.INSTANCE.transform( THE_UUID ) )
41+
.setParameter( 2, "John Doe" )
42+
.executeUpdate();
3943
} );
4044
}
4145

@@ -49,22 +53,22 @@ void tearDown(SessionFactoryScope scope) {
4953
@Test
5054
@JiraKey( value = "HHH-17246" )
5155
public void testTrailingZeroByteTruncation(SessionFactoryScope scope) {
52-
scope.inSession(
53-
session -> assertEquals( 15, session.createNativeQuery("select id from Book", byte[].class).getSingleResult().length )
54-
);
5556
scope.inTransaction(
5657
session -> {
58+
// Assert that our assumption is correct i.e. Sybase truncates trailing zero bytes
59+
assertEquals( 15, session.createNativeQuery("select id from book", byte[].class).getSingleResult().length );
5760
Book b = session.createQuery( "from Book", Book.class ).getSingleResult();
58-
assertEquals(uuid, b.id);
61+
assertEquals( THE_UUID, b.id );
5962
}
6063
);
6164
}
6265

6366
@Entity(name = "Book")
67+
@Table(name = "book")
6468
static class Book {
6569
@Id
6670
// The purpose is to effectively provoke the trailing 0 bytes truncation
67-
@JdbcType( SybaseUuidAsVarbinaryJdbcType.class )
71+
@Column(columnDefinition = "varbinary(16)")
6872
UUID id;
6973

7074
String author;

Diff for: hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/SybaseUuidAsVarbinaryJdbcType.java

-18
This file was deleted.

0 commit comments

Comments
 (0)