12
12
import java .sql .ResultSet ;
13
13
import java .sql .SQLException ;
14
14
import java .sql .Types ;
15
+ import java .util .Locale ;
15
16
import java .util .Properties ;
16
17
import javax .persistence .Enumerated ;
17
18
import javax .persistence .MapKeyEnumerated ;
@@ -61,138 +62,17 @@ public class EnumType implements EnhancedUserType, DynamicParameterizedType,Logg
61
62
62
63
private Class <? extends Enum > enumClass ;
63
64
private EnumValueMapper enumValueMapper ;
64
- private int sqlType = Types .INTEGER ; // before any guessing
65
-
66
- @ Override
67
- public int [] sqlTypes () {
68
- return new int [] { sqlType };
69
- }
70
-
71
- @ Override
72
- public Class <? extends Enum > returnedClass () {
73
- return enumClass ;
74
- }
75
-
76
- @ Override
77
- public boolean equals (Object x , Object y ) throws HibernateException {
78
- return x == y ;
79
- }
80
-
81
- @ Override
82
- public int hashCode (Object x ) throws HibernateException {
83
- return x == null ? 0 : x .hashCode ();
84
- }
85
-
86
- @ Override
87
- public Object nullSafeGet (ResultSet rs , String [] names , SessionImplementor session , Object owner ) throws SQLException {
88
- if ( enumValueMapper == null ) {
89
- resolveEnumValueMapper ( rs , names [0 ] );
90
- }
91
- return enumValueMapper .getValue ( rs , names );
92
- }
93
-
94
- private void resolveEnumValueMapper (ResultSet rs , String name ) {
95
- if ( enumValueMapper == null ) {
96
- try {
97
- resolveEnumValueMapper ( rs .getMetaData ().getColumnType ( rs .findColumn ( name ) ) );
98
- }
99
- catch (Exception e ) {
100
- // because some drivers do not implement this
101
- LOG .debugf (
102
- "JDBC driver threw exception calling java.sql.ResultSetMetaData.getColumnType; " +
103
- "using fallback determination [%s] : %s" ,
104
- enumClass .getName (),
105
- e .getMessage ()
106
- );
107
- // peek at the result value to guess type (this is legacy behavior)
108
- try {
109
- Object value = rs .getObject ( name );
110
- if ( Number .class .isInstance ( value ) ) {
111
- treatAsOrdinal ();
112
- }
113
- else {
114
- treatAsNamed ();
115
- }
116
- }
117
- catch (SQLException ignore ) {
118
- treatAsOrdinal ();
119
- }
120
- }
121
- }
122
- }
123
-
124
- private void resolveEnumValueMapper (int columnType ) {
125
- // fallback for cases where not enough parameter/parameterization information was passed in
126
- if ( isOrdinal ( columnType ) ) {
127
- treatAsOrdinal ();
128
- }
129
- else {
130
- treatAsNamed ();
131
- }
132
- }
133
-
134
- @ Override
135
- public void nullSafeSet (PreparedStatement st , Object value , int index , SessionImplementor session ) throws HibernateException , SQLException {
136
- if ( enumValueMapper == null ) {
137
- resolveEnumValueMapper ( st , index );
138
- }
139
- enumValueMapper .setValue ( st , (Enum ) value , index );
140
- }
141
-
142
- private void resolveEnumValueMapper (PreparedStatement st , int index ) {
143
- if ( enumValueMapper == null ) {
144
- try {
145
- resolveEnumValueMapper ( st .getParameterMetaData ().getParameterType ( index ) );
146
- }
147
- catch (Exception e ) {
148
- // because some drivers do not implement this
149
- LOG .debugf (
150
- "JDBC driver threw exception calling java.sql.ParameterMetaData#getParameterType; " +
151
- "falling back to ordinal-based enum mapping [%s] : %s" ,
152
- enumClass .getName (),
153
- e .getMessage ()
154
- );
155
- // Originally, this was simply treatAsOrdinal(). But, for DBs that do not implement the above, enums
156
- // were treated as ordinal even when the *.hbm.xml explicitly define the type sqlCode. By default,
157
- // this is essentially the same anyway, since sqlType is defaulted to Integer.
158
- resolveEnumValueMapper ( sqlType );
159
- }
160
- }
161
- }
162
-
163
- @ Override
164
- public Object deepCopy (Object value ) throws HibernateException {
165
- return value ;
166
- }
167
-
168
- @ Override
169
- public boolean isMutable () {
170
- return false ;
171
- }
172
-
173
- @ Override
174
- public Serializable disassemble (Object value ) throws HibernateException {
175
- return ( Serializable ) value ;
176
- }
177
-
178
- @ Override
179
- public Object assemble (Serializable cached , Object owner ) throws HibernateException {
180
- return cached ;
181
- }
182
-
183
- @ Override
184
- public Object replace (Object original , Object target , Object owner ) throws HibernateException {
185
- return original ;
186
- }
65
+ private int sqlType ;
187
66
188
67
@ Override
189
68
public void setParameterValues (Properties parameters ) {
69
+ // IMPL NOTE: we handle 2 distinct cases here:
70
+ // 1) we are passed a ParameterType instance in the incoming Properties - generally
71
+ // speaking this indicates the annotation-binding case, and the passed ParameterType
72
+ // represents information about the attribute and annotation
73
+ // 2) we are not passed a ParameterType - generally this indicates a hbm.xml binding case.
190
74
final ParameterType reader = (ParameterType ) parameters .get ( PARAMETER_TYPE );
191
75
192
- // IMPL NOTE : be protective about not setting enumValueMapper (i.e. calling treatAsNamed/treatAsOrdinal)
193
- // in cases where we do not have enough information. In such cases we do additional checks
194
- // as part of nullSafeGet/nullSafeSet to query against the JDBC metadata to make the determination.
195
-
196
76
if ( reader != null ) {
197
77
enumClass = reader .getReturnedClass ().asSubclass ( Enum .class );
198
78
@@ -212,52 +92,24 @@ else if ( javax.persistence.EnumType.STRING.equals( enumType ) ) {
212
92
}
213
93
214
94
if ( isOrdinal ) {
215
- treatAsOrdinal ();
95
+ this . enumValueMapper = new OrdinalEnumValueMapper ();
216
96
}
217
97
else {
218
- treatAsNamed ();
98
+ this . enumValueMapper = new NamedEnumValueMapper ();
219
99
}
220
100
sqlType = enumValueMapper .getSqlType ();
221
101
}
222
102
else {
223
- String enumClassName = (String ) parameters .get ( ENUM );
103
+ final String enumClassName = (String ) parameters .get ( ENUM );
224
104
try {
225
105
enumClass = ReflectHelper .classForName ( enumClassName , this .getClass () ).asSubclass ( Enum .class );
226
106
}
227
107
catch ( ClassNotFoundException exception ) {
228
- throw new HibernateException ( "Enum class not found" , exception );
108
+ throw new HibernateException ( "Enum class not found: " + enumClassName , exception );
229
109
}
230
110
231
- final Object useNamedSetting = parameters .get ( NAMED );
232
- if ( useNamedSetting != null ) {
233
- final boolean useNamed = ConfigurationHelper .getBoolean ( NAMED , parameters );
234
- if ( useNamed ) {
235
- treatAsNamed ();
236
- }
237
- else {
238
- treatAsOrdinal ();
239
- }
240
- sqlType = enumValueMapper .getSqlType ();
241
- }
242
- }
243
-
244
- final String type = (String ) parameters .get ( TYPE );
245
- if ( type != null ) {
246
- sqlType = Integer .decode ( type );
247
- }
248
- }
249
-
250
- private void treatAsOrdinal () {
251
- if ( enumValueMapper == null || ! OrdinalEnumValueMapper .class .isInstance ( enumValueMapper ) ) {
252
- enumValueMapper = new OrdinalEnumValueMapper ();
253
- sqlType = enumValueMapper .getSqlType ();
254
- }
255
- }
256
-
257
- private void treatAsNamed () {
258
- if ( enumValueMapper == null || ! NamedEnumValueMapper .class .isInstance ( enumValueMapper ) ) {
259
- enumValueMapper = new NamedEnumValueMapper ();
260
- sqlType = enumValueMapper .getSqlType ();
111
+ this .enumValueMapper = interpretParameters ( parameters );
112
+ this .sqlType = enumValueMapper .getSqlType ();
261
113
}
262
114
}
263
115
@@ -287,6 +139,131 @@ private <T extends Annotation> T getAnnotation(Annotation[] annotations, Class<T
287
139
return null ;
288
140
}
289
141
142
+ private EnumValueMapper interpretParameters (Properties parameters ) {
143
+ if ( parameters .containsKey ( NAMED ) ) {
144
+ final boolean useNamed = ConfigurationHelper .getBoolean ( NAMED , parameters );
145
+ if ( useNamed ) {
146
+ return new NamedEnumValueMapper ();
147
+ }
148
+ else {
149
+ return new OrdinalEnumValueMapper ();
150
+ }
151
+ }
152
+
153
+ if ( parameters .containsKey ( TYPE ) ) {
154
+ final int type = Integer .decode ( (String ) parameters .get ( TYPE ) );
155
+ if ( isNumericType ( type ) ) {
156
+ return new OrdinalEnumValueMapper ();
157
+ }
158
+ else if ( isCharacterType ( type ) ) {
159
+ return new OrdinalEnumValueMapper ();
160
+ }
161
+ else {
162
+ throw new HibernateException (
163
+ String .format (
164
+ Locale .ENGLISH ,
165
+ "Passed JDBC type code [%s] not recognized as numeric nor character" ,
166
+ type
167
+ )
168
+ );
169
+ }
170
+ }
171
+
172
+ // the fallback
173
+ return new OrdinalEnumValueMapper ();
174
+ }
175
+
176
+ private boolean isCharacterType (int jdbcTypeCode ) {
177
+ switch ( jdbcTypeCode ) {
178
+ case Types .CHAR :
179
+ case Types .LONGVARCHAR :
180
+ case Types .VARCHAR : {
181
+ return true ;
182
+ }
183
+ default : {
184
+ return false ;
185
+ }
186
+ }
187
+ }
188
+
189
+ private boolean isNumericType (int jdbcTypeCode ) {
190
+ switch ( jdbcTypeCode ) {
191
+ case Types .INTEGER :
192
+ case Types .NUMERIC :
193
+ case Types .SMALLINT :
194
+ case Types .TINYINT :
195
+ case Types .BIGINT :
196
+ case Types .DECIMAL :
197
+ case Types .DOUBLE :
198
+ case Types .FLOAT : {
199
+ return true ;
200
+ }
201
+ default :
202
+ return false ;
203
+ }
204
+ }
205
+
206
+ @ Override
207
+ public int [] sqlTypes () {
208
+ return new int [] { sqlType };
209
+ }
210
+
211
+ @ Override
212
+ public Class <? extends Enum > returnedClass () {
213
+ return enumClass ;
214
+ }
215
+
216
+ @ Override
217
+ public boolean equals (Object x , Object y ) throws HibernateException {
218
+ return x == y ;
219
+ }
220
+
221
+ @ Override
222
+ public int hashCode (Object x ) throws HibernateException {
223
+ return x == null ? 0 : x .hashCode ();
224
+ }
225
+
226
+ @ Override
227
+ public Object nullSafeGet (ResultSet rs , String [] names , SessionImplementor session , Object owner ) throws SQLException {
228
+ if ( enumValueMapper == null ) {
229
+ throw new AssertionFailure ( "EnumType (" + enumClass .getName () + ") not properly, fully configured" );
230
+ }
231
+ return enumValueMapper .getValue ( rs , names );
232
+ }
233
+
234
+ @ Override
235
+ public void nullSafeSet (PreparedStatement st , Object value , int index , SessionImplementor session ) throws HibernateException , SQLException {
236
+ if ( enumValueMapper == null ) {
237
+ throw new AssertionFailure ( "EnumType (" + enumClass .getName () + ") not properly, fully configured" );
238
+ }
239
+ enumValueMapper .setValue ( st , (Enum ) value , index );
240
+ }
241
+
242
+ @ Override
243
+ public Object deepCopy (Object value ) throws HibernateException {
244
+ return value ;
245
+ }
246
+
247
+ @ Override
248
+ public boolean isMutable () {
249
+ return false ;
250
+ }
251
+
252
+ @ Override
253
+ public Serializable disassemble (Object value ) throws HibernateException {
254
+ return ( Serializable ) value ;
255
+ }
256
+
257
+ @ Override
258
+ public Object assemble (Serializable cached , Object owner ) throws HibernateException {
259
+ return cached ;
260
+ }
261
+
262
+ @ Override
263
+ public Object replace (Object original , Object target , Object owner ) throws HibernateException {
264
+ return original ;
265
+ }
266
+
290
267
@ Override
291
268
public String objectToSQLString (Object value ) {
292
269
return enumValueMapper .objectToSQLString ( (Enum ) value );
@@ -310,14 +287,18 @@ public String toLoggableString(Object value, SessionFactoryImplementor factory)
310
287
return value .toString ();
311
288
}
312
289
313
- private static interface EnumValueMapper extends Serializable {
314
- public int getSqlType ();
315
- public Enum getValue (ResultSet rs , String [] names ) throws SQLException ;
316
- public void setValue (PreparedStatement st , Enum value , int index ) throws SQLException ;
290
+ public boolean isOrdinal () {
291
+ return enumValueMapper instanceof OrdinalEnumValueMapper ;
292
+ }
293
+
294
+ private interface EnumValueMapper extends Serializable {
295
+ int getSqlType ();
296
+ Enum getValue (ResultSet rs , String [] names ) throws SQLException ;
297
+ void setValue (PreparedStatement st , Enum value , int index ) throws SQLException ;
317
298
318
- public String objectToSQLString (Enum value );
319
- public String toXMLString (Enum value );
320
- public Enum fromXMLString (String xml );
299
+ String objectToSQLString (Enum value );
300
+ String toXMLString (Enum value );
301
+ Enum fromXMLString (String xml );
321
302
}
322
303
323
304
public abstract class EnumValueMapperSupport implements EnumValueMapper {
@@ -479,27 +460,4 @@ protected Object extractJdbcValue(Enum value) {
479
460
}
480
461
}
481
462
482
- public boolean isOrdinal () {
483
- return isOrdinal ( sqlType );
484
- }
485
-
486
- private boolean isOrdinal (int paramType ) {
487
- switch ( paramType ) {
488
- case Types .INTEGER :
489
- case Types .NUMERIC :
490
- case Types .SMALLINT :
491
- case Types .TINYINT :
492
- case Types .BIGINT :
493
- case Types .DECIMAL : //for Oracle Driver
494
- case Types .DOUBLE : //for Oracle Driver
495
- case Types .FLOAT : //for Oracle Driver
496
- return true ;
497
- case Types .CHAR :
498
- case Types .LONGVARCHAR :
499
- case Types .VARCHAR :
500
- return false ;
501
- default :
502
- throw new HibernateException ( "Unable to persist an Enum in a column of SQL Type: " + paramType );
503
- }
504
- }
505
463
}
0 commit comments