View Javadoc
1   /*
2    *    Copyright 2006-2026 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    *       https://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.mybatis.generator.internal.types;
17  
18  import static org.mybatis.generator.internal.util.StringUtility.isTrue;
19  
20  import java.math.BigDecimal;
21  import java.sql.Types;
22  import java.time.LocalDate;
23  import java.time.LocalDateTime;
24  import java.time.LocalTime;
25  import java.time.OffsetDateTime;
26  import java.time.OffsetTime;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Optional;
31  import java.util.Properties;
32  
33  import org.jspecify.annotations.Nullable;
34  import org.mybatis.generator.api.IntrospectedColumn;
35  import org.mybatis.generator.api.JavaTypeResolver;
36  import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
37  import org.mybatis.generator.config.Context;
38  import org.mybatis.generator.config.PropertyRegistry;
39  
40  public class JavaTypeResolverDefaultImpl implements JavaTypeResolver {
41  
42      protected @Nullable List<String> warnings;
43  
44      protected final Properties properties;
45  
46      protected @Nullable Context context;
47  
48      protected boolean forceBigDecimals;
49  
50      protected final Map<Integer, JdbcTypeInformation> typeMap;
51  
52      public JavaTypeResolverDefaultImpl() {
53          super();
54          properties = new Properties();
55          typeMap = new HashMap<>();
56  
57          typeMap.put(Types.ARRAY, new JdbcTypeInformation("ARRAY", //$NON-NLS-1$
58                  new FullyQualifiedJavaType(Object.class.getName())));
59          typeMap.put(Types.BIGINT, new JdbcTypeInformation("BIGINT", //$NON-NLS-1$
60                  new FullyQualifiedJavaType(Long.class.getName())));
61          typeMap.put(Types.BINARY, new JdbcTypeInformation("BINARY", //$NON-NLS-1$
62                  new FullyQualifiedJavaType("byte[]"))); //$NON-NLS-1$
63          typeMap.put(Types.BIT, new JdbcTypeInformation("BIT", //$NON-NLS-1$
64                  new FullyQualifiedJavaType(Boolean.class.getName())));
65          typeMap.put(Types.BLOB, new JdbcTypeInformation("BLOB", //$NON-NLS-1$
66                  new FullyQualifiedJavaType("byte[]"))); //$NON-NLS-1$
67          typeMap.put(Types.BOOLEAN, new JdbcTypeInformation("BOOLEAN", //$NON-NLS-1$
68                  new FullyQualifiedJavaType(Boolean.class.getName())));
69          typeMap.put(Types.CHAR, new JdbcTypeInformation("CHAR", //$NON-NLS-1$
70                  new FullyQualifiedJavaType(String.class.getName())));
71          typeMap.put(Types.CLOB, new JdbcTypeInformation("CLOB", //$NON-NLS-1$
72                  new FullyQualifiedJavaType(String.class.getName())));
73          typeMap.put(Types.DATALINK, new JdbcTypeInformation("DATALINK", //$NON-NLS-1$
74                  new FullyQualifiedJavaType(Object.class.getName())));
75          typeMap.put(Types.DATE, new JdbcTypeInformation("DATE", //$NON-NLS-1$
76                  new FullyQualifiedJavaType(LocalDate.class.getName())));
77          typeMap.put(Types.DECIMAL, new JdbcTypeInformation("DECIMAL", //$NON-NLS-1$
78                  new FullyQualifiedJavaType(BigDecimal.class.getName())));
79          typeMap.put(Types.DISTINCT, new JdbcTypeInformation("DISTINCT", //$NON-NLS-1$
80                  new FullyQualifiedJavaType(Object.class.getName())));
81          typeMap.put(Types.DOUBLE, new JdbcTypeInformation("DOUBLE", //$NON-NLS-1$
82                  new FullyQualifiedJavaType(Double.class.getName())));
83          typeMap.put(Types.FLOAT, new JdbcTypeInformation("FLOAT", //$NON-NLS-1$
84                  new FullyQualifiedJavaType(Double.class.getName())));
85          typeMap.put(Types.INTEGER, new JdbcTypeInformation("INTEGER", //$NON-NLS-1$
86                  new FullyQualifiedJavaType(Integer.class.getName())));
87          typeMap.put(Types.JAVA_OBJECT, new JdbcTypeInformation("JAVA_OBJECT", //$NON-NLS-1$
88                  new FullyQualifiedJavaType(Object.class.getName())));
89          typeMap.put(Types.LONGNVARCHAR, new JdbcTypeInformation("LONGNVARCHAR", //$NON-NLS-1$
90                  new FullyQualifiedJavaType(String.class.getName())));
91          typeMap.put(Types.LONGVARBINARY, new JdbcTypeInformation("LONGVARBINARY", //$NON-NLS-1$
92                  new FullyQualifiedJavaType("byte[]"))); //$NON-NLS-1$
93          typeMap.put(Types.LONGVARCHAR, new JdbcTypeInformation("LONGVARCHAR", //$NON-NLS-1$
94                  new FullyQualifiedJavaType(String.class.getName())));
95          typeMap.put(Types.NCHAR, new JdbcTypeInformation("NCHAR", //$NON-NLS-1$
96                  new FullyQualifiedJavaType(String.class.getName())));
97          typeMap.put(Types.NCLOB, new JdbcTypeInformation("NCLOB", //$NON-NLS-1$
98                  new FullyQualifiedJavaType(String.class.getName())));
99          typeMap.put(Types.NVARCHAR, new JdbcTypeInformation("NVARCHAR", //$NON-NLS-1$
100                 new FullyQualifiedJavaType(String.class.getName())));
101         typeMap.put(Types.NULL, new JdbcTypeInformation("NULL", //$NON-NLS-1$
102                 new FullyQualifiedJavaType(Object.class.getName())));
103         typeMap.put(Types.NUMERIC, new JdbcTypeInformation("NUMERIC", //$NON-NLS-1$
104                 new FullyQualifiedJavaType(BigDecimal.class.getName())));
105         typeMap.put(Types.OTHER, new JdbcTypeInformation("OTHER", //$NON-NLS-1$
106                 new FullyQualifiedJavaType(Object.class.getName())));
107         typeMap.put(Types.REAL, new JdbcTypeInformation("REAL", //$NON-NLS-1$
108                 new FullyQualifiedJavaType(Float.class.getName())));
109         typeMap.put(Types.REF, new JdbcTypeInformation("REF", //$NON-NLS-1$
110                 new FullyQualifiedJavaType(Object.class.getName())));
111         typeMap.put(Types.SMALLINT, new JdbcTypeInformation("SMALLINT", //$NON-NLS-1$
112                 new FullyQualifiedJavaType(Short.class.getName())));
113         typeMap.put(Types.SQLXML, new JdbcTypeInformation("SQLXML", //$NON-NLS-1$
114                 new FullyQualifiedJavaType(String.class.getName())));
115         typeMap.put(Types.STRUCT, new JdbcTypeInformation("STRUCT", //$NON-NLS-1$
116                 new FullyQualifiedJavaType(Object.class.getName())));
117         typeMap.put(Types.TIME, new JdbcTypeInformation("TIME", //$NON-NLS-1$
118                 new FullyQualifiedJavaType(LocalTime.class.getName())));
119         typeMap.put(Types.TIMESTAMP, new JdbcTypeInformation("TIMESTAMP", //$NON-NLS-1$
120                 new FullyQualifiedJavaType(LocalDateTime.class.getName())));
121         typeMap.put(Types.TINYINT, new JdbcTypeInformation("TINYINT", //$NON-NLS-1$
122                 new FullyQualifiedJavaType(Byte.class.getName())));
123         typeMap.put(Types.VARBINARY, new JdbcTypeInformation("VARBINARY", //$NON-NLS-1$
124                 new FullyQualifiedJavaType("byte[]"))); //$NON-NLS-1$
125         typeMap.put(Types.VARCHAR, new JdbcTypeInformation("VARCHAR", //$NON-NLS-1$
126                 new FullyQualifiedJavaType(String.class.getName())));
127         // JDK 1.8 types
128         typeMap.put(Types.TIME_WITH_TIMEZONE, new JdbcTypeInformation("TIME_WITH_TIMEZONE", //$NON-NLS-1$
129                 new FullyQualifiedJavaType(OffsetTime.class.getName())));
130         typeMap.put(Types.TIMESTAMP_WITH_TIMEZONE, new JdbcTypeInformation("TIMESTAMP_WITH_TIMEZONE", //$NON-NLS-1$
131                 new FullyQualifiedJavaType(OffsetDateTime.class.getName())));
132     }
133 
134     @Override
135     public void addConfigurationProperties(Properties properties) {
136         this.properties.putAll(properties);
137         forceBigDecimals = isTrue(properties.getProperty(PropertyRegistry.TYPE_RESOLVER_FORCE_BIG_DECIMALS));
138     }
139 
140     @Override
141     public Optional<JdbcTypeInformation> calculateTypeInformation(IntrospectedColumn introspectedColumn) {
142         JdbcTypeInformation jdbcTypeInformation = typeMap.get(introspectedColumn.getJdbcType());
143         if (jdbcTypeInformation != null) {
144             return Optional.of(overrideDefault(introspectedColumn, jdbcTypeInformation));
145         } else {
146             return Optional.empty();
147         }
148     }
149 
150     protected JdbcTypeInformation overrideDefault(IntrospectedColumn column,
151                                                   JdbcTypeInformation defaultTypeInformation) {
152         return switch (column.getJdbcType()) {
153         case Types.BIT -> overrideBitType(column, defaultTypeInformation);
154         case Types.DECIMAL, Types.NUMERIC -> overrideBigDecimalType(column, defaultTypeInformation);
155         default -> defaultTypeInformation;
156         };
157     }
158 
159     protected JdbcTypeInformation overrideBitType(IntrospectedColumn column,
160                                                   JdbcTypeInformation defaultTypeInformation) {
161         if (column.getLength() > 1) {
162             return defaultTypeInformation.withJavaType(new FullyQualifiedJavaType("byte[]")); //$NON-NLS-1$
163         } else {
164             return defaultTypeInformation;
165         }
166     }
167 
168     protected JdbcTypeInformation overrideBigDecimalType(IntrospectedColumn column,
169                                                          JdbcTypeInformation defaultTypeInformation) {
170         if (column.getScale() > 0 || column.getLength() > 18 || forceBigDecimals) {
171             return defaultTypeInformation;
172         } else if (column.getLength() > 9) {
173             return defaultTypeInformation.withJavaType(new FullyQualifiedJavaType(Long.class.getName()));
174         } else if (column.getLength() > 4) {
175             return defaultTypeInformation.withJavaType(new FullyQualifiedJavaType(Integer.class.getName()));
176         } else {
177             return defaultTypeInformation.withJavaType(new FullyQualifiedJavaType(Short.class.getName()));
178         }
179     }
180 
181     @Override
182     public void setWarnings(List<String> warnings) {
183         this.warnings = warnings;
184     }
185 
186     @Override
187     public void setContext(Context context) {
188         this.context = context;
189     }
190 }