001/** 002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.table; 017 018import com.mybatisflex.annotation.*; 019import com.mybatisflex.core.FlexConsts; 020import com.mybatisflex.core.FlexGlobalConfig; 021import com.mybatisflex.core.exception.FlexExceptions; 022import com.mybatisflex.core.util.ClassUtil; 023import com.mybatisflex.core.util.CollectionUtil; 024import com.mybatisflex.core.util.StringUtil; 025import org.apache.ibatis.reflection.Reflector; 026import org.apache.ibatis.session.Configuration; 027import org.apache.ibatis.type.JdbcType; 028import org.apache.ibatis.type.TypeHandler; 029import org.apache.ibatis.type.TypeHandlerRegistry; 030import org.apache.ibatis.type.UnknownTypeHandler; 031import org.apache.ibatis.util.MapUtil; 032 033import java.lang.reflect.Field; 034import java.lang.reflect.Modifier; 035import java.lang.reflect.ParameterizedType; 036import java.lang.reflect.Type; 037import java.math.BigDecimal; 038import java.math.BigInteger; 039import java.sql.Time; 040import java.sql.Timestamp; 041import java.time.*; 042import java.time.chrono.JapaneseDate; 043import java.util.*; 044import java.util.concurrent.ConcurrentHashMap; 045import java.util.stream.Collectors; 046 047public class TableInfoFactory { 048 049 050 private static final Set<Class<?>> defaultSupportColumnTypes = CollectionUtil.newHashSet( 051 int.class, Integer.class, 052 short.class, Short.class, 053 long.class, Long.class, 054 float.class, Float.class, 055 double.class, Double.class, 056 boolean.class, Boolean.class, 057 Date.class, java.sql.Date.class, Time.class, Timestamp.class, 058 Instant.class, LocalDate.class, LocalDateTime.class, LocalTime.class, OffsetDateTime.class, OffsetTime.class, ZonedDateTime.class, 059 Year.class, Month.class, YearMonth.class, JapaneseDate.class, 060 byte[].class, Byte[].class, 061 BigInteger.class, BigDecimal.class, 062 char.class, String.class, Character.class 063 ); 064 065 066 private static final Map<Class<?>, TableInfo> mapperTableInfoMap = new ConcurrentHashMap<>(); 067 private static final Map<Class<?>, TableInfo> entityTableMap = new ConcurrentHashMap<>(); 068 private static final Map<String, TableInfo> tableInfoMap = new ConcurrentHashMap<>(); 069 070 071 public static TableInfo ofMapperClass(Class<?> mapperClass) { 072 return MapUtil.computeIfAbsent(mapperTableInfoMap, mapperClass, key -> { 073 Class<?> entityClass = getEntityClass(mapperClass); 074 if (entityClass == null) { 075 return null; 076 } 077 return ofEntityClass(entityClass); 078 }); 079 } 080 081 082 public static TableInfo ofEntityClass(Class<?> entityClass) { 083 return MapUtil.computeIfAbsent(entityTableMap, entityClass, aClass -> { 084 TableInfo tableInfo = createTableInfo(entityClass); 085 tableInfoMap.put(tableInfo.getTableName(), tableInfo); 086 return tableInfo; 087 }); 088 } 089 090 091 public static TableInfo ofTableName(String tableName) { 092 return StringUtil.isNotBlank(tableName) ? tableInfoMap.get(tableName) : null; 093 } 094 095 096 private static Class<?> getEntityClass(Class<?> mapperClass) { 097 if (mapperClass == null || mapperClass == Object.class) { 098 return null; 099 } 100 Type[] genericInterfaces = mapperClass.getGenericInterfaces(); 101 if (genericInterfaces.length == 1) { 102 Type type = genericInterfaces[0]; 103 if (type instanceof ParameterizedType) { 104 Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0]; 105 return actualTypeArgument instanceof Class ? (Class<?>) actualTypeArgument : null; 106 } else if (type instanceof Class) { 107 return getEntityClass((Class<?>) type); 108 } 109 } 110 return getEntityClass(mapperClass.getSuperclass()); 111 } 112 113 114 private static TableInfo createTableInfo(Class<?> entityClass) { 115 116 TableInfo tableInfo = new TableInfo(); 117 tableInfo.setEntityClass(entityClass); 118 tableInfo.setReflector(new Reflector(entityClass)); 119 120 121 //初始化表名 122 Table table = entityClass.getAnnotation(Table.class); 123 if (table != null) { 124 tableInfo.setTableName(table.value()); 125 tableInfo.setSchema(table.schema()); 126 tableInfo.setCamelToUnderline(table.camelToUnderline()); 127 128 if (table.onInsert().length > 0) { 129 List<InsertListener> insertListeners = Arrays.stream(table.onInsert()) 130 .filter(listener -> listener != NoneListener.class) 131 .map(ClassUtil::newInstance) 132 .collect(Collectors.toList()); 133 tableInfo.setOnInsertListeners(insertListeners); 134 } 135 136 if (table.onUpdate().length > 0) { 137 List<UpdateListener> updateListeners = Arrays.stream(table.onUpdate()) 138 .filter(listener -> listener != NoneListener.class) 139 .map(ClassUtil::newInstance) 140 .collect(Collectors.toList()); 141 tableInfo.setOnUpdateListeners(updateListeners); 142 } 143 144 if (table.onSet().length > 0) { 145 List<SetListener> setListeners = Arrays.stream(table.onSet()) 146 .filter(listener -> listener != NoneListener.class) 147 .map(ClassUtil::newInstance) 148 .collect(Collectors.toList()); 149 tableInfo.setOnSetListeners(setListeners); 150 } 151 152 if (StringUtil.isNotBlank(table.dataSource())) { 153 tableInfo.setDataSource(table.dataSource()); 154 } 155 } else { 156 //默认为类名转驼峰下划线 157 String tableName = StringUtil.camelToUnderline(entityClass.getSimpleName()); 158 tableInfo.setTableName(tableName); 159 } 160 161 //初始化字段相关 162 List<ColumnInfo> columnInfoList = new ArrayList<>(); 163 List<IdInfo> idInfos = new ArrayList<>(); 164 165 Field idField = null; 166 167 String logicDeleteColumn = null; 168 String versionColumn = null; 169 String tenantIdColumn = null; 170 171 //数据插入时,默认插入数据字段 172 Map<String, String> onInsertColumns = new HashMap<>(); 173 174 //数据更新时,默认更新内容的字段 175 Map<String, String> onUpdateColumns = new HashMap<>(); 176 177 //大字段列 178 Set<String> largeColumns = new LinkedHashSet<>(); 179 // 默认查询列 180 Set<String> defaultColumns = new LinkedHashSet<>(); 181 182 183 List<Field> entityFields = ClassUtil.getAllFields(entityClass); 184 for (Field field : entityFields) { 185 186 Column column = field.getAnnotation(Column.class); 187 if (column != null && column.ignore()) { 188 continue; // ignore 189 } 190 191 192 if (Modifier.isStatic(field.getModifiers())) { 193 //ignore static field 194 continue; 195 } 196 197 198 //未配置 typeHandler 的情况下,只支持基本数据类型,不支持比如 list set 或者自定义的类等 199 if ((column == null || column.typeHandler() == UnknownTypeHandler.class) 200 && !field.getType().isEnum() 201 && !defaultSupportColumnTypes.contains(field.getType())) { 202 continue; 203 } 204 205 206 //列名 207 String columnName = column != null && StringUtil.isNotBlank(column.value()) 208 ? column.value() 209 : (tableInfo.isCamelToUnderline() ? StringUtil.camelToUnderline(field.getName()) : field.getName()); 210 211 //逻辑删除字段 212 if (column != null && column.isLogicDelete()) { 213 if (logicDeleteColumn == null) { 214 logicDeleteColumn = columnName; 215 } else { 216 throw FlexExceptions.wrap("The logic delete column of entity[%s] must be less then 2.", entityClass.getName()); 217 } 218 } 219 220 //乐观锁版本字段 221 if (column != null && column.version()) { 222 if (versionColumn == null) { 223 versionColumn = columnName; 224 } else { 225 throw FlexExceptions.wrap("The version column of entity[%s] must be less then 2.", entityClass.getName()); 226 } 227 } 228 229 //租户ID 字段 230 if (column != null && column.tenantId()) { 231 if (tenantIdColumn == null) { 232 tenantIdColumn = columnName; 233 } else { 234 throw FlexExceptions.wrap("The tenantId column of entity[%s] must be less then 2.", entityClass.getName()); 235 } 236 } 237 238 if (column != null && StringUtil.isNotBlank(column.onInsertValue())) { 239 onInsertColumns.put(columnName, column.onInsertValue().trim()); 240 } 241 242 243 if (column != null && StringUtil.isNotBlank(column.onUpdateValue())) { 244 onUpdateColumns.put(columnName, column.onUpdateValue().trim()); 245 } 246 247 248 if (column != null && column.isLarge()) { 249 largeColumns.add(columnName); 250 } 251 252 if (column == null || !column.isLarge()) { 253 defaultColumns.add(columnName); 254 } 255 256 Id id = field.getAnnotation(Id.class); 257 ColumnInfo columnInfo; 258 if (id != null) { 259 columnInfo = new IdInfo(columnName, field.getName(), field.getType(), id); 260 idInfos.add((IdInfo) columnInfo); 261 } else { 262 columnInfo = new ColumnInfo(); 263 columnInfoList.add(columnInfo); 264 } 265 266 columnInfo.setColumn(columnName); 267 columnInfo.setProperty(field.getName()); 268 columnInfo.setPropertyType(field.getType()); 269 270 if (column != null && column.typeHandler() != UnknownTypeHandler.class) { 271 Class<?> typeHandlerClass = column.typeHandler(); 272 Configuration configuration = FlexGlobalConfig.getDefaultConfig().getConfiguration(); 273 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 274 TypeHandler<?> typeHandler = typeHandlerRegistry.getInstance(columnInfo.getPropertyType(), typeHandlerClass); 275 columnInfo.setTypeHandler(typeHandler); 276 } 277 278 ColumnMask columnMask = field.getAnnotation(ColumnMask.class); 279 if (columnMask != null && StringUtil.isNotBlank(columnMask.value())) { 280 if (String.class != field.getType()) { 281 throw new IllegalStateException("@ColumnMask() only support for string type field. error: " + entityClass.getName() + "." + field.getName()); 282 } 283 columnInfo.setMaskType(columnMask.value().trim()); 284 } 285 286 if (column != null && column.jdbcType() != JdbcType.UNDEFINED) { 287 columnInfo.setJdbcType(column.jdbcType()); 288 } 289 290 if (FlexConsts.DEFAULT_PRIMARY_FIELD.equals(field.getName())) { 291 idField = field; 292 } 293 } 294 295 296 if (idInfos.isEmpty() && idField != null) { 297 int index = -1; 298 for (int i = 0; i < columnInfoList.size(); i++) { 299 ColumnInfo columnInfo = columnInfoList.get(i); 300 if (FlexConsts.DEFAULT_PRIMARY_FIELD.equals(columnInfo.getProperty())) { 301 index = i; 302 break; 303 } 304 } 305 if (index >= 0) { 306 ColumnInfo removedColumnInfo = columnInfoList.remove(index); 307 idInfos.add(new IdInfo(removedColumnInfo)); 308 } 309 } 310 311 tableInfo.setLogicDeleteColumn(logicDeleteColumn); 312 tableInfo.setVersionColumn(versionColumn); 313 tableInfo.setTenantIdColumn(tenantIdColumn); 314 315 if (!onInsertColumns.isEmpty()) { 316 tableInfo.setOnInsertColumns(onInsertColumns); 317 } 318 319 if (!onUpdateColumns.isEmpty()) { 320 tableInfo.setOnUpdateColumns(onUpdateColumns); 321 } 322 323 if (!largeColumns.isEmpty()) { 324 tableInfo.setLargeColumns(largeColumns.toArray(new String[0])); 325 } 326 if (!defaultColumns.isEmpty()) { 327 tableInfo.setDefaultColumns(defaultColumns.toArray(new String[0])); 328 } 329 330 tableInfo.setColumnInfoList(columnInfoList); 331 tableInfo.setPrimaryKeyList(idInfos); 332 333 334 return tableInfo; 335 } 336}