6
6
package org .hibernate .reactive .provider .impl ;
7
7
8
8
import java .lang .reflect .Type ;
9
+ import java .sql .CallableStatement ;
10
+ import java .sql .PreparedStatement ;
11
+ import java .sql .SQLException ;
12
+ import java .sql .Timestamp ;
9
13
import java .sql .Types ;
14
+ import java .time .Instant ;
15
+ import java .time .ZonedDateTime ;
16
+ import java .util .Calendar ;
17
+ import java .util .TimeZone ;
10
18
11
19
import org .hibernate .boot .model .TypeContributions ;
12
20
import org .hibernate .boot .model .TypeContributor ;
13
21
import org .hibernate .dialect .Dialect ;
22
+ import org .hibernate .dialect .MySQLDialect ;
14
23
import org .hibernate .dialect .PostgreSQLDialect ;
15
24
import org .hibernate .engine .jdbc .env .spi .JdbcEnvironment ;
25
+ import org .hibernate .reactive .adaptor .impl .PreparedStatementAdaptor ;
26
+ import org .hibernate .reactive .dialect .ReactiveDialectWrapper ;
16
27
import org .hibernate .service .ServiceRegistry ;
17
28
import org .hibernate .type .AbstractSingleColumnStandardBasicType ;
18
29
import org .hibernate .type .BasicTypeRegistry ;
24
35
import org .hibernate .type .descriptor .java .PrimitiveByteArrayJavaType ;
25
36
import org .hibernate .type .descriptor .java .StringJavaType ;
26
37
import org .hibernate .type .descriptor .java .spi .JavaTypeRegistry ;
38
+ import org .hibernate .type .descriptor .jdbc .BasicBinder ;
27
39
import org .hibernate .type .descriptor .jdbc .BlobJdbcType ;
28
40
import org .hibernate .type .descriptor .jdbc .ClobJdbcType ;
29
41
import org .hibernate .type .descriptor .jdbc .JdbcType ;
30
42
import org .hibernate .type .descriptor .jdbc .JdbcTypeIndicators ;
31
43
import org .hibernate .type .descriptor .jdbc .ObjectJdbcType ;
44
+ import org .hibernate .type .descriptor .jdbc .TimestampJdbcType ;
45
+ import org .hibernate .type .descriptor .jdbc .TimestampUtcAsJdbcTimestampJdbcType ;
46
+ import org .hibernate .type .descriptor .jdbc .spi .JdbcTypeRegistry ;
32
47
import org .hibernate .type .descriptor .sql .DdlType ;
33
48
import org .hibernate .type .descriptor .sql .spi .DdlTypeRegistry ;
49
+ import org .hibernate .type .spi .TypeConfiguration ;
34
50
35
51
import io .vertx .core .json .JsonObject ;
36
52
@@ -52,21 +68,112 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv
52
68
}
53
69
54
70
private void registerReactiveChanges (TypeContributions typeContributions , ServiceRegistry serviceRegistry ) {
55
- BasicTypeRegistry basicTypeRegistry = typeContributions .getTypeConfiguration ().getBasicTypeRegistry ();
56
- DdlTypeRegistry ddlTypeRegistry = typeContributions .getTypeConfiguration ().getDdlTypeRegistry ();
71
+ Dialect dialect = dialect ( serviceRegistry );
72
+ TypeConfiguration typeConfiguration = typeContributions .getTypeConfiguration ();
73
+
74
+ DdlTypeRegistry ddlTypeRegistry = typeConfiguration .getDdlTypeRegistry ();
57
75
ddlTypeRegistry .addDescriptor ( Types .JAVA_OBJECT , new JavaObjectDdlType () );
58
76
59
- JavaTypeRegistry javaTypeRegistry = typeContributions . getTypeConfiguration () .getJavaTypeRegistry ();
77
+ JavaTypeRegistry javaTypeRegistry = typeConfiguration .getJavaTypeRegistry ();
60
78
javaTypeRegistry .addDescriptor ( JsonObjectJavaType .INSTANCE );
61
79
62
- Dialect dialect = serviceRegistry .getService ( JdbcEnvironment .class ).getDialect ();
80
+ if ( dialect instanceof MySQLDialect ) {
81
+ JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration .getJdbcTypeRegistry ();
82
+ jdbcTypeRegistry .addDescriptor ( TimestampAsLocalDateTimeJdbcType .INSTANCE );
83
+ jdbcTypeRegistry .addDescriptor ( TimestampUtcAsLocalDateTimeJdbcType .INSTANCE );
84
+ }
85
+
86
+ BasicTypeRegistry basicTypeRegistry = typeConfiguration .getBasicTypeRegistry ();
63
87
basicTypeRegistry .register ( new JsonType ( dialect ) );
64
88
// FIXME: I think only Postgres, needs this special type because of the way the driver returns the SQL type
65
89
// We could only add them for Postgres
66
90
basicTypeRegistry .register ( new BlobType ( dialect ) );
67
91
basicTypeRegistry .register ( new ClobType ( dialect ) );
68
92
}
69
93
94
+ private Dialect dialect (ServiceRegistry serviceRegistry ) {
95
+ Dialect dialect = serviceRegistry .getService ( JdbcEnvironment .class ).getDialect ();
96
+ return dialect instanceof ReactiveDialectWrapper
97
+ ? ( (ReactiveDialectWrapper ) dialect ).getWrappedDialect ()
98
+ : dialect ;
99
+ }
100
+
101
+ /**
102
+ * Some database (MySQL for example) don't like saving temporal types with a timezone.
103
+ *
104
+ * @see TimestampJdbcType
105
+ */
106
+ private static class TimestampAsLocalDateTimeJdbcType extends TimestampJdbcType {
107
+ public static final TimestampAsLocalDateTimeJdbcType INSTANCE = new TimestampAsLocalDateTimeJdbcType ();
108
+
109
+ @ Override
110
+ public <X > ValueBinder <X > getBinder (final JavaType <X > javaType ) {
111
+ return new BasicBinder <>( javaType , this ) {
112
+ @ Override
113
+ protected void doBind (PreparedStatement st , X value , int index , WrapperOptions options ) throws SQLException {
114
+ final Timestamp timestamp = javaType .unwrap ( value , Timestamp .class , options );
115
+ if ( value instanceof Calendar ) {
116
+ ( (PreparedStatementAdaptor ) st )
117
+ .setTimestamp ( index , timestamp , (Calendar ) value , ZonedDateTime ::toLocalDateTime );
118
+ }
119
+ else if ( options .getJdbcTimeZone () != null ) {
120
+ ( (PreparedStatementAdaptor ) st )
121
+ .setTimestamp ( index , timestamp , Calendar .getInstance ( options .getJdbcTimeZone () ), ZonedDateTime ::toLocalDateTime );
122
+ }
123
+ else {
124
+ st .setTimestamp ( index , timestamp );
125
+ }
126
+ }
127
+
128
+ @ Override
129
+ protected void doBind (CallableStatement st , X value , String name , WrapperOptions options )
130
+ throws SQLException {
131
+ final Timestamp timestamp = javaType .unwrap ( value , Timestamp .class , options );
132
+ if ( value instanceof Calendar ) {
133
+ ( (PreparedStatementAdaptor ) st )
134
+ .setTimestamp ( name , timestamp , (Calendar ) value , ZonedDateTime ::toLocalDateTime );
135
+ }
136
+ else if ( options .getJdbcTimeZone () != null ) {
137
+ ( (PreparedStatementAdaptor ) st )
138
+ .setTimestamp ( name , timestamp , Calendar .getInstance ( options .getJdbcTimeZone () ), ZonedDateTime ::toLocalDateTime );
139
+ }
140
+ else {
141
+ st .setTimestamp ( name , timestamp );
142
+ }
143
+ }
144
+ };
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Some database (MySQL for example) don't like saving temporal types with a timezone.
150
+ *
151
+ * @see TimestampUtcAsJdbcTimestampJdbcType
152
+ */
153
+ private static class TimestampUtcAsLocalDateTimeJdbcType extends TimestampUtcAsJdbcTimestampJdbcType {
154
+ private static final Calendar UTC_CALENDAR = Calendar .getInstance ( TimeZone .getTimeZone ( "UTC" ) );
155
+
156
+ public static final TimestampUtcAsLocalDateTimeJdbcType INSTANCE = new TimestampUtcAsLocalDateTimeJdbcType ();
157
+
158
+ @ Override
159
+ public <X > ValueBinder <X > getBinder (final JavaType <X > javaType ) {
160
+ return new BasicBinder <>( javaType , this ) {
161
+ @ Override
162
+ protected void doBind (PreparedStatement st , X value , int index , WrapperOptions options ) throws SQLException {
163
+ final Instant instant = javaType .unwrap ( value , Instant .class , options );
164
+ ( (PreparedStatementAdaptor ) st ).setTimestamp ( index , Timestamp .from ( instant ), UTC_CALENDAR , ZonedDateTime ::toLocalDateTime );
165
+ }
166
+
167
+ @ Override
168
+ protected void doBind (CallableStatement st , X value , String name , WrapperOptions options )
169
+ throws SQLException {
170
+ final Instant instant = javaType .unwrap ( value , Instant .class , options );
171
+ ( (PreparedStatementAdaptor ) st ).setTimestamp ( name , Timestamp .from ( instant ), UTC_CALENDAR , ZonedDateTime ::toLocalDateTime );
172
+ }
173
+ };
174
+ }
175
+ }
176
+
70
177
private static class JavaObjectDdlType implements DdlType {
71
178
72
179
@ Override
0 commit comments