View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.type;
17  
18  import java.math.BigDecimal;
19  import java.math.BigInteger;
20  import java.net.URL;
21  import java.sql.Array;
22  import java.sql.CallableStatement;
23  import java.sql.PreparedStatement;
24  import java.sql.ResultSet;
25  import java.sql.SQLException;
26  import java.sql.Time;
27  import java.sql.Timestamp;
28  import java.time.LocalDate;
29  import java.time.LocalDateTime;
30  import java.time.LocalTime;
31  import java.time.OffsetDateTime;
32  import java.time.OffsetTime;
33  import java.util.Calendar;
34  import java.util.concurrent.ConcurrentHashMap;
35  
36  /**
37   * @author Clinton Begin
38   */
39  public class ArrayTypeHandler extends BaseTypeHandler<Object> {
40  
41    private static final ConcurrentHashMap<Class<?>, String> STANDARD_MAPPING;
42  
43    static {
44      STANDARD_MAPPING = new ConcurrentHashMap<>();
45      STANDARD_MAPPING.put(BigDecimal.class, JdbcType.NUMERIC.name());
46      STANDARD_MAPPING.put(BigInteger.class, JdbcType.BIGINT.name());
47      STANDARD_MAPPING.put(boolean.class, JdbcType.BOOLEAN.name());
48      STANDARD_MAPPING.put(Boolean.class, JdbcType.BOOLEAN.name());
49      STANDARD_MAPPING.put(byte[].class, JdbcType.VARBINARY.name());
50      STANDARD_MAPPING.put(byte.class, JdbcType.TINYINT.name());
51      STANDARD_MAPPING.put(Byte.class, JdbcType.TINYINT.name());
52      STANDARD_MAPPING.put(Calendar.class, JdbcType.TIMESTAMP.name());
53      STANDARD_MAPPING.put(java.sql.Date.class, JdbcType.DATE.name());
54      STANDARD_MAPPING.put(java.util.Date.class, JdbcType.TIMESTAMP.name());
55      STANDARD_MAPPING.put(double.class, JdbcType.DOUBLE.name());
56      STANDARD_MAPPING.put(Double.class, JdbcType.DOUBLE.name());
57      STANDARD_MAPPING.put(float.class, JdbcType.REAL.name());
58      STANDARD_MAPPING.put(Float.class, JdbcType.REAL.name());
59      STANDARD_MAPPING.put(int.class, JdbcType.INTEGER.name());
60      STANDARD_MAPPING.put(Integer.class, JdbcType.INTEGER.name());
61      STANDARD_MAPPING.put(LocalDate.class, JdbcType.DATE.name());
62      STANDARD_MAPPING.put(LocalDateTime.class, JdbcType.TIMESTAMP.name());
63      STANDARD_MAPPING.put(LocalTime.class, JdbcType.TIME.name());
64      STANDARD_MAPPING.put(long.class, JdbcType.BIGINT.name());
65      STANDARD_MAPPING.put(Long.class, JdbcType.BIGINT.name());
66      STANDARD_MAPPING.put(OffsetDateTime.class, JdbcType.TIMESTAMP_WITH_TIMEZONE.name());
67      STANDARD_MAPPING.put(OffsetTime.class, JdbcType.TIME_WITH_TIMEZONE.name());
68      STANDARD_MAPPING.put(Short.class, JdbcType.SMALLINT.name());
69      STANDARD_MAPPING.put(String.class, JdbcType.VARCHAR.name());
70      STANDARD_MAPPING.put(Time.class, JdbcType.TIME.name());
71      STANDARD_MAPPING.put(Timestamp.class, JdbcType.TIMESTAMP.name());
72      STANDARD_MAPPING.put(URL.class, JdbcType.DATALINK.name());
73    }
74  
75    public ArrayTypeHandler() {
76      super();
77    }
78  
79    @Override
80    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
81        throws SQLException {
82      if (parameter instanceof Array) {
83        // it's the user's responsibility to properly free() the Array instance
84        ps.setArray(i, (Array) parameter);
85      } else {
86        if (!parameter.getClass().isArray()) {
87          throw new TypeException(
88              "ArrayType Handler requires SQL array or java array parameter and does not support type "
89                  + parameter.getClass());
90        }
91        Class<?> componentType = parameter.getClass().getComponentType();
92        String arrayTypeName = resolveTypeName(componentType);
93        Array array = ps.getConnection().createArrayOf(arrayTypeName, (Object[]) parameter);
94        ps.setArray(i, array);
95        array.free();
96      }
97    }
98  
99    protected String resolveTypeName(Class<?> type) {
100     return STANDARD_MAPPING.getOrDefault(type, JdbcType.JAVA_OBJECT.name());
101   }
102 
103   @Override
104   public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
105     return extractArray(rs.getArray(columnName));
106   }
107 
108   @Override
109   public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
110     return extractArray(rs.getArray(columnIndex));
111   }
112 
113   @Override
114   public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
115     return extractArray(cs.getArray(columnIndex));
116   }
117 
118   protected Object extractArray(Array array) throws SQLException {
119     if (array == null) {
120       return null;
121     }
122     Object result = array.getArray();
123     array.free();
124     return result;
125   }
126 
127 }