15
15
*/
16
16
package org .springframework .data .jdbc .core .dialect ;
17
17
18
+ import java .sql .Array ;
18
19
import java .sql .JDBCType ;
20
+ import java .sql .SQLException ;
19
21
import java .sql .SQLType ;
22
+ import java .sql .Types ;
23
+ import java .util .Arrays ;
24
+ import java .util .Collections ;
25
+ import java .util .HashMap ;
26
+ import java .util .Iterator ;
27
+ import java .util .Map ;
28
+ import java .util .UUID ;
20
29
30
+ import org .postgresql .core .Oid ;
31
+ import org .postgresql .jdbc .TypeInfoCache ;
21
32
import org .springframework .data .jdbc .core .convert .JdbcArrayColumns ;
22
33
import org .springframework .data .relational .core .dialect .PostgresDialect ;
34
+ import org .springframework .util .ClassUtils ;
23
35
24
36
/**
25
37
* JDBC specific Postgres Dialect.
26
38
*
27
39
* @author Jens Schauder
40
+ * @author Mark Paluch
28
41
* @since 2.3
29
42
*/
30
43
public class JdbcPostgresDialect extends PostgresDialect implements JdbcDialect {
@@ -40,11 +53,31 @@ public JdbcArrayColumns getArraySupport() {
40
53
41
54
static class JdbcPostgresArrayColumns implements JdbcArrayColumns {
42
55
56
+ private static final boolean TYPE_INFO_PRESENT = ClassUtils .isPresent ("org.postgresql.jdbc.TypeInfoCache" ,
57
+ JdbcPostgresDialect .class .getClassLoader ());
58
+
59
+ private static final TypeInfoWrapper TYPE_INFO_WRAPPER ;
60
+
61
+ static {
62
+ TYPE_INFO_WRAPPER = TYPE_INFO_PRESENT ? new TypeInfoCacheWrapper () : new TypeInfoWrapper ();
63
+ }
64
+
43
65
@ Override
44
66
public boolean isSupported () {
45
67
return true ;
46
68
}
47
69
70
+ @ Override
71
+ public SQLType getSqlType (Class <?> componentType ) {
72
+
73
+ SQLType sqlType = TYPE_INFO_WRAPPER .getArrayTypeMap ().get (componentType );
74
+ if (sqlType != null ) {
75
+ return sqlType ;
76
+ }
77
+
78
+ return JdbcArrayColumns .super .getSqlType (componentType );
79
+ }
80
+
48
81
@ Override
49
82
public String getArrayTypeName (SQLType jdbcType ) {
50
83
@@ -58,4 +91,92 @@ public String getArrayTypeName(SQLType jdbcType) {
58
91
return jdbcType .getName ();
59
92
}
60
93
}
94
+
95
+ /**
96
+ * Wrapper for Postgres types. Defaults to no-op to guard runtimes against absent TypeInfoCache.
97
+ *
98
+ * @since 3.1.3
99
+ */
100
+ static class TypeInfoWrapper {
101
+
102
+ /**
103
+ * @return a type map between a Java array component type and its Postgres type.
104
+ */
105
+ Map <Class <?>, SQLType > getArrayTypeMap () {
106
+ return Collections .emptyMap ();
107
+ }
108
+ }
109
+
110
+ /**
111
+ * {@link TypeInfoWrapper} backed by {@link TypeInfoCache}.
112
+ *
113
+ * @since 3.1.3
114
+ */
115
+ static class TypeInfoCacheWrapper extends TypeInfoWrapper {
116
+
117
+ private final Map <Class <?>, SQLType > arrayTypes = new HashMap <>();
118
+
119
+ public TypeInfoCacheWrapper () {
120
+
121
+ TypeInfoCache cache = new TypeInfoCache (null , 0 );
122
+ addWellKnownTypes (cache );
123
+
124
+ Iterator <String > it = cache .getPGTypeNamesWithSQLTypes ();
125
+
126
+ try {
127
+
128
+ while (it .hasNext ()) {
129
+
130
+ String pgTypeName = it .next ();
131
+ int oid = cache .getPGType (pgTypeName );
132
+ String javaClassName = cache .getJavaClass (oid );
133
+ int arrayOid = cache .getJavaArrayType (pgTypeName );
134
+
135
+ if (!ClassUtils .isPresent (javaClassName , getClass ().getClassLoader ())) {
136
+ continue ;
137
+ }
138
+
139
+ Class <?> javaClass = ClassUtils .forName (javaClassName , getClass ().getClassLoader ());
140
+
141
+ // avoid accidental usage of smaller database types that map to the same Java type or generic-typed SQL
142
+ // arrays.
143
+ if (javaClass == Array .class || javaClass == String .class || javaClass == Integer .class || oid == Oid .OID
144
+ || oid == Oid .MONEY ) {
145
+ continue ;
146
+ }
147
+
148
+ arrayTypes .put (javaClass , new PGSQLType (pgTypeName , arrayOid ));
149
+ }
150
+ } catch (SQLException | ClassNotFoundException e ) {
151
+ throw new IllegalStateException ("Cannot create type info mapping" , e );
152
+ }
153
+ }
154
+
155
+ private static void addWellKnownTypes (TypeInfoCache cache ) {
156
+ cache .addCoreType ("uuid" , Oid .UUID , Types .OTHER , UUID .class .getName (), Oid .UUID_ARRAY );
157
+ }
158
+
159
+ @ Override
160
+ Map <Class <?>, SQLType > getArrayTypeMap () {
161
+ return arrayTypes ;
162
+ }
163
+
164
+ record PGSQLType (String name , int oid ) implements SQLType {
165
+
166
+ @ Override
167
+ public String getName () {
168
+ return name ;
169
+ }
170
+
171
+ @ Override
172
+ public String getVendor () {
173
+ return "Postgres" ;
174
+ }
175
+
176
+ @ Override
177
+ public Integer getVendorTypeNumber () {
178
+ return oid ;
179
+ }
180
+ }
181
+ }
61
182
}
0 commit comments