Skip to content

Commit 16d0f7e

Browse files
blafondDavideD
authored andcommitted
[#1732] Fix to support array Types
1 parent 1195039 commit 16d0f7e

File tree

7 files changed

+307
-26
lines changed

7 files changed

+307
-26
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.adaptor.impl;
7+
8+
import java.sql.Array;
9+
import java.sql.ResultSet;
10+
import java.util.Map;
11+
12+
import org.hibernate.type.descriptor.jdbc.JdbcType;
13+
14+
public class ArrayAdaptor implements Array {
15+
16+
private final String baseTypeName;
17+
private final int jdbcTypeCode;
18+
private Object[] objects;
19+
20+
public ArrayAdaptor(JdbcType elementJdbcType, Object[] objects) {
21+
this( elementJdbcType.getFriendlyName(), elementJdbcType.getJdbcTypeCode(), objects );
22+
}
23+
24+
public ArrayAdaptor(String baseTypeName, Object[] objects) {
25+
this( baseTypeName, 0, objects );
26+
}
27+
28+
private ArrayAdaptor(String baseTypeName, int jdbcTypeCode, Object[] objects) {
29+
this.baseTypeName = baseTypeName;
30+
this.jdbcTypeCode = jdbcTypeCode;
31+
this.objects = objects;
32+
}
33+
34+
@Override
35+
public String getBaseTypeName() {
36+
return baseTypeName;
37+
}
38+
39+
@Override
40+
public int getBaseType() {
41+
return jdbcTypeCode;
42+
}
43+
44+
@Override
45+
public Object getArray() {
46+
return objects;
47+
}
48+
49+
@Override
50+
public Object getArray(Map<String, Class<?>> map) {
51+
throw new UnsupportedOperationException( "array of maps is not yet supported" );
52+
}
53+
54+
@Override
55+
public Object getArray(long index, int count) {
56+
throw new UnsupportedOperationException( "array of maps is not yet supported" );
57+
}
58+
59+
@Override
60+
public Object getArray(long index, int count, Map<String, Class<?>> map) {
61+
throw new UnsupportedOperationException( "array of maps is not yet supported" );
62+
}
63+
64+
@Override
65+
public ResultSet getResultSet() {
66+
throw new UnsupportedOperationException( "array of maps is not yet supported" );
67+
}
68+
69+
@Override
70+
public ResultSet getResultSet(Map<String, Class<?>> map) {
71+
throw new UnsupportedOperationException( "array of maps is not yet supported" );
72+
}
73+
74+
@Override
75+
public ResultSet getResultSet(long index, int count) {
76+
throw new UnsupportedOperationException( "array of maps is not yet supported" );
77+
}
78+
79+
@Override
80+
public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) {
81+
throw new UnsupportedOperationException( "array of maps is not yet supported" );
82+
}
83+
84+
@Override
85+
public void free() {
86+
objects = null;
87+
}
88+
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public void setString(int parameterIndex, String x) {
146146

147147
@Override
148148
public void setBytes(int parameterIndex, byte[] x) {
149-
put( parameterIndex, Buffer.buffer(x) );
149+
put( parameterIndex, Buffer.buffer( x ) );
150150
}
151151

152152
@Override
@@ -245,7 +245,12 @@ public void setClob(int parameterIndex, Clob x) {
245245

246246
@Override
247247
public void setArray(int parameterIndex, Array x) {
248-
put( parameterIndex, x );
248+
try {
249+
put( parameterIndex, x.getArray() );
250+
}
251+
catch (SQLException e) {
252+
throw new AssertionFailure( "This shouldn't have happened" );
253+
}
249254
}
250255

251256
@Override
@@ -260,7 +265,7 @@ public void setDate(int parameterIndex, Date x, Calendar cal) {
260265

261266
@Override
262267
public void setNull(int parameterIndex, int sqlType, String typeName) {
263-
throw new UnsupportedOperationException();
268+
put( parameterIndex, new JdbcNull( sqlType ) );
264269
}
265270

266271
@Override

hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
import org.hibernate.engine.jdbc.BlobProxy;
3737
import org.hibernate.engine.jdbc.ClobProxy;
38+
import org.hibernate.type.descriptor.jdbc.JdbcType;
3839

3940
import io.vertx.core.buffer.Buffer;
4041
import io.vertx.sqlclient.Row;
@@ -748,6 +749,15 @@ public Array getArray(int columnIndex) {
748749
throw new UnsupportedOperationException();
749750
}
750751

752+
public Array getArray(int columnIndex, JdbcType elementJdbcType) {
753+
Object[] objects = (Object[]) row.getValue( columnIndex - 1 );
754+
wasNull = objects == null;
755+
if ( objects == null ) {
756+
return null;
757+
}
758+
return new ArrayAdaptor( elementJdbcType, objects );
759+
}
760+
751761
@Override
752762
public Object getObject(String columnLabel, Map<String, Class<?>> map) {
753763
throw new UnsupportedOperationException();

hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveTypeContributor.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.hibernate.dialect.PostgreSQLDialect;
2828
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
2929
import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor;
30-
import org.hibernate.reactive.type.descriptor.jdbc.ReactiveArrayJdbcType;
30+
import org.hibernate.reactive.type.descriptor.jdbc.ReactiveArrayJdbcTypeConstructor;
3131
import org.hibernate.service.ServiceRegistry;
3232
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
3333
import org.hibernate.type.BasicTypeRegistry;
@@ -45,7 +45,6 @@
4545
import org.hibernate.type.descriptor.jdbc.JdbcType;
4646
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
4747
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
48-
import org.hibernate.type.descriptor.jdbc.ReactiveArrayJdbcTypeConstructor;
4948
import org.hibernate.type.descriptor.jdbc.TimestampJdbcType;
5049
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
5150
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
@@ -83,7 +82,6 @@ private void registerReactiveChanges(TypeContributions typeContributions, Servic
8382
javaTypeRegistry.addDescriptor( JsonObjectJavaType.INSTANCE );
8483

8584
JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
86-
jdbcTypeRegistry.addDescriptor( ReactiveArrayJdbcType.INSTANCE );
8785
jdbcTypeRegistry.addTypeConstructor( ReactiveArrayJdbcTypeConstructor.INSTANCE );
8886

8987
if ( dialect instanceof MySQLDialect || dialect instanceof DB2Dialect || dialect instanceof OracleDialect ) {

hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcType.java

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@
77

88
import java.lang.reflect.Array;
99
import java.sql.CallableStatement;
10+
import java.sql.Date;
1011
import java.sql.PreparedStatement;
1112
import java.sql.ResultSet;
1213
import java.sql.SQLException;
14+
import java.sql.Time;
15+
import java.sql.Timestamp;
1316
import java.sql.Types;
17+
import java.time.LocalDate;
18+
import java.time.LocalDateTime;
19+
import java.time.LocalTime;
1420

15-
import org.hibernate.HibernateException;
21+
import org.hibernate.reactive.adaptor.impl.ArrayAdaptor;
22+
import org.hibernate.reactive.adaptor.impl.ResultSetAdaptor;
1623
import org.hibernate.type.descriptor.ValueBinder;
1724
import org.hibernate.type.descriptor.ValueExtractor;
1825
import org.hibernate.type.descriptor.WrapperOptions;
@@ -24,19 +31,21 @@
2431
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
2532
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
2633
import org.hibernate.type.descriptor.jdbc.JdbcType;
27-
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
2834
import org.hibernate.type.descriptor.jdbc.internal.JdbcLiteralFormatterArray;
2935
import org.hibernate.type.spi.TypeConfiguration;
3036

37+
38+
3139
/**
3240
* {@link java.sql.Connection} has a method {@link java.sql.Connection#createArrayOf(String, Object[])}, but we don't have
3341
* it in Vert.x SQL Client.
42+
* <p>
43+
* Plus, the Vert.x SQL client accept arrays as parameters.
44+
* </p>
3445
*
3546
* @see org.hibernate.type.descriptor.jdbc.ArrayJdbcType
3647
*/
37-
public class ReactiveArrayJdbcType implements JdbcType {
38-
39-
public static final ReactiveArrayJdbcType INSTANCE = new ReactiveArrayJdbcType( ObjectJdbcType.INSTANCE );
48+
public class ReactiveArrayJdbcType implements JdbcType {
4049

4150
private final JdbcType elementJdbcType;
4251

@@ -88,20 +97,64 @@ public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
8897
@Override
8998
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
9099
throws SQLException {
91-
st.setObject( index, value );
100+
101+
ArrayAdaptor arrayObject = getArrayObject( value, options );
102+
st.setArray( index, arrayObject );
92103
}
93104

94105
@Override
95-
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options){
96-
try {
97-
st.setObject( name, value );
106+
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) {
107+
throw new UnsupportedOperationException();
108+
}
109+
110+
private ArrayAdaptor getArrayObject(X value, WrapperOptions options) {
111+
final TypeConfiguration typeConfiguration = options.getSessionFactory().getTypeConfiguration();
112+
ReactiveArrayJdbcType jdbcType = (ReactiveArrayJdbcType) getJdbcType();
113+
final JdbcType elementJdbcType = jdbcType.getElementJdbcType();
114+
final JdbcType underlyingJdbcType = typeConfiguration.getJdbcTypeRegistry()
115+
.getDescriptor( elementJdbcType.getDefaultSqlTypeCode() );
116+
final Class<?> elementJdbcJavaTypeClass = elementJdbcJavaTypeClass(
117+
options,
118+
elementJdbcType,
119+
underlyingJdbcType,
120+
typeConfiguration
121+
);
122+
//noinspection unchecked
123+
final Class<Object[]> arrayClass = (Class<Object[]>) Array.newInstance( elementJdbcJavaTypeClass, 0 )
124+
.getClass();
125+
final Object[] objects = getJavaType().unwrap( value, arrayClass, options );
126+
return new ArrayAdaptor( elementJdbcType, objects );
127+
}
128+
129+
private Class<?> elementJdbcJavaTypeClass(
130+
WrapperOptions options,
131+
JdbcType elementJdbcType,
132+
JdbcType underlyingJdbcType,
133+
TypeConfiguration typeConfiguration) {
134+
final Class<?> preferredJavaTypeClass = elementJdbcType.getPreferredJavaTypeClass( options );
135+
final Class<?> elementJdbcJavaTypeClass;
136+
if ( preferredJavaTypeClass == null ) {
137+
elementJdbcJavaTypeClass = underlyingJdbcType
138+
.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration )
139+
.getJavaTypeClass();
98140
}
99-
catch (SQLException ex) {
100-
throw new HibernateException(
101-
"JDBC driver does not support named parameters for setArray. Use positional.",
102-
ex
103-
);
141+
else {
142+
elementJdbcJavaTypeClass = preferredJavaTypeClass;
104143
}
144+
return convertTypes( elementJdbcJavaTypeClass );
145+
}
146+
147+
private Class<?> convertTypes(Class<?> elementJdbcJavaTypeClass) {
148+
if ( Timestamp.class.equals( elementJdbcJavaTypeClass ) ) {
149+
return LocalDateTime.class;
150+
}
151+
if ( Date.class.equals( elementJdbcJavaTypeClass ) ) {
152+
return LocalDate.class;
153+
}
154+
if ( Time.class.equals( elementJdbcJavaTypeClass ) ) {
155+
return LocalTime.class;
156+
}
157+
return elementJdbcJavaTypeClass;
105158
}
106159
};
107160
}
@@ -110,13 +163,19 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions
110163
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaTypeDescriptor) {
111164
return new BasicExtractor<>( javaTypeDescriptor, this ) {
112165
@Override
113-
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
114-
return javaTypeDescriptor.wrap( rs.getArray( paramIndex ), options );
166+
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) {
167+
return javaTypeDescriptor.wrap(
168+
( (ResultSetAdaptor) rs ).getArray( paramIndex, elementJdbcType ),
169+
options
170+
);
115171
}
116172

117173
@Override
118-
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
119-
return javaTypeDescriptor.wrap( statement.getArray( index ), options );
174+
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) {
175+
return javaTypeDescriptor.wrap(
176+
( (ResultSetAdaptor) statement ).getArray( index, elementJdbcType ),
177+
options
178+
);
120179
}
121180

122181
@Override
@@ -132,6 +191,10 @@ public String getFriendlyName() {
132191
return "ARRAY";
133192
}
134193

194+
public JdbcType getElementJdbcType() {
195+
return elementJdbcType;
196+
}
197+
135198
@Override
136199
public String toString() {
137200
return ReactiveArrayJdbcType.class.getSimpleName() + "(" + getFriendlyName() + ")";

hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcTypeConstructor.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,40 @@
33
* SPDX-License-Identifier: Apache-2.0
44
* Copyright: Red Hat Inc. and Hibernate Authors
55
*/
6-
package org.hibernate.type.descriptor.jdbc;
6+
package org.hibernate.reactive.type.descriptor.jdbc;
77

88
import java.sql.Types;
99

1010
import org.hibernate.dialect.Dialect;
11-
import org.hibernate.reactive.type.descriptor.jdbc.ReactiveArrayJdbcType;
11+
import org.hibernate.dialect.OracleDialect;
1212
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
1313
import org.hibernate.type.BasicType;
14+
import org.hibernate.type.descriptor.jdbc.JdbcType;
15+
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
1416
import org.hibernate.type.spi.TypeConfiguration;
1517

18+
import static org.hibernate.dialect.DialectDelegateWrapper.extractRealDialect;
19+
1620
/**
1721
* Factory for {@link ReactiveArrayJdbcType}.
1822
*/
1923
public class ReactiveArrayJdbcTypeConstructor implements JdbcTypeConstructor {
2024
public static final ReactiveArrayJdbcTypeConstructor INSTANCE = new ReactiveArrayJdbcTypeConstructor();
2125

26+
@Override
2227
public JdbcType resolveType(
2328
TypeConfiguration typeConfiguration,
2429
Dialect dialect,
2530
BasicType<?> elementType,
2631
ColumnTypeInformation columnTypeInformation) {
32+
Dialect realDialect = extractRealDialect( dialect );
33+
if ( realDialect instanceof OracleDialect ) {
34+
String typeName = columnTypeInformation == null ? null : columnTypeInformation.getTypeName();
35+
if ( typeName == null || typeName.isBlank() ) {
36+
typeName = ReactiveOracleArrayJdbcType.getTypeName( elementType.getJavaTypeDescriptor(), dialect );
37+
}
38+
return new ReactiveOracleArrayJdbcType( elementType.getJdbcType(), typeName );
39+
}
2740
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
2841
}
2942

@@ -33,6 +46,14 @@ public JdbcType resolveType(
3346
Dialect dialect,
3447
JdbcType elementType,
3548
ColumnTypeInformation columnTypeInformation) {
49+
Dialect realDialect = extractRealDialect( dialect );
50+
if ( realDialect instanceof OracleDialect ) {
51+
// a bit wrong, since columnTypeInformation.getTypeName() is typically null!
52+
return new ReactiveOracleArrayJdbcType(
53+
elementType,
54+
columnTypeInformation == null ? null : columnTypeInformation.getTypeName()
55+
);
56+
}
3657
return new ReactiveArrayJdbcType( elementType );
3758
}
3859

0 commit comments

Comments
 (0)