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; 045 046public class TableInfoFactory { 047 048 049 private static final Set<Class<?>> defaultSupportColumnTypes = CollectionUtil.newHashSet( 050 int.class, Integer.class, 051 short.class, Short.class, 052 long.class, Long.class, 053 float.class, Float.class, 054 double.class, Double.class, 055 boolean.class, Boolean.class, 056 Date.class, java.sql.Date.class, Time.class, Timestamp.class, 057 Instant.class, LocalDate.class, LocalDateTime.class, LocalTime.class, OffsetDateTime.class, OffsetTime.class, ZonedDateTime.class, 058 Year.class, Month.class, YearMonth.class, JapaneseDate.class, 059 byte[].class, Byte[].class, 060 BigInteger.class, BigDecimal.class, 061 char.class, String.class, Character.class 062 ); 063 064 065 private static final Map<Class<?>, TableInfo> mapperTableInfoMap = new ConcurrentHashMap<>(); 066 private static final Map<Class<?>, TableInfo> entityTableMap = new ConcurrentHashMap<>(); 067 private static final Map<String, TableInfo> tableInfoMap = new ConcurrentHashMap<>(); 068 069 070 public static TableInfo ofMapperClass(Class<?> mapperClass) { 071 return MapUtil.computeIfAbsent(mapperTableInfoMap, mapperClass, key -> { 072 Class<?> entityClass = getEntityClass(mapperClass); 073 if (entityClass == null) { 074 return null; 075 } 076 return ofEntityClass(entityClass); 077 }); 078 } 079 080 081 public static TableInfo ofEntityClass(Class<?> entityClass) { 082 return MapUtil.computeIfAbsent(entityTableMap, entityClass, aClass -> { 083 TableInfo tableInfo = createTableInfo(entityClass); 084 tableInfoMap.put(tableInfo.getTableName(), tableInfo); 085 return tableInfo; 086 }); 087 } 088 089 090 public static TableInfo ofTableName(String tableName) { 091 return tableInfoMap.get(tableName); 092 } 093 094 095 private static Class<?> getEntityClass(Class<?> mapperClass) { 096 Type[] genericInterfaces = mapperClass.getGenericInterfaces(); 097 if (genericInterfaces.length == 1) { 098 Type type = genericInterfaces[0]; 099 if (type instanceof ParameterizedType) { 100 return (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0]; 101 } else { 102 return getEntityClass((Class<?>) type); 103 } 104 } 105 return null; 106 } 107 108 109 private static TableInfo createTableInfo(Class<?> entityClass) { 110 111 TableInfo tableInfo = new TableInfo(); 112 tableInfo.setEntityClass(entityClass); 113 tableInfo.setReflector(new Reflector(entityClass)); 114 115 116 //初始化表名 117 Table table = entityClass.getAnnotation(Table.class); 118 if (table != null) { 119 tableInfo.setTableName(table.value()); 120 tableInfo.setSchema(table.schema()); 121 tableInfo.setCamelToUnderline(table.camelToUnderline()); 122 123 if (table.onInsert() != NoneListener.class) { 124 tableInfo.setOnInsertListener(ClassUtil.newInstance(table.onInsert())); 125 } 126 127 if (table.onUpdate() != NoneListener.class) { 128 tableInfo.setOnUpdateListener(ClassUtil.newInstance(table.onUpdate())); 129 } 130 131 if (table.onSet() != NoneListener.class) { 132 tableInfo.setOnSetListener(ClassUtil.newInstance(table.onSet())); 133 } 134 135 if (StringUtil.isNotBlank(table.dataSource())) { 136 tableInfo.setDataSource(table.dataSource()); 137 } 138 } else { 139 //默认为类名转驼峰下划线 140 String tableName = StringUtil.camelToUnderline(entityClass.getSimpleName()); 141 tableInfo.setTableName(tableName); 142 } 143 144 //初始化字段相关 145 List<ColumnInfo> columnInfoList = new ArrayList<>(); 146 List<IdInfo> idInfos = new ArrayList<>(); 147 148 Field idField = null; 149 150 String logicDeleteColumn = null; 151 String versionColumn = null; 152 String tenantIdColumn = null; 153 154 //数据插入时,默认插入数据字段 155 Map<String, String> onInsertColumns = new HashMap<>(); 156 157 //数据更新时,默认更新内容的字段 158 Map<String, String> onUpdateColumns = new HashMap<>(); 159 160 //大字段列 161 Set<String> largeColumns = new LinkedHashSet<>(); 162 163 164 List<Field> entityFields = ClassUtil.getAllFields(entityClass); 165 for (Field field : entityFields) { 166 167 Column column = field.getAnnotation(Column.class); 168 if (column != null && column.ignore()) { 169 continue; // ignore 170 } 171 172 173 if (Modifier.isStatic(field.getModifiers())) { 174 //ignore static field 175 continue; 176 } 177 178 179 //未配置 typeHandler 的情况下,只支持基本数据类型,不支持比如 list set 或者自定义的类等 180 if ((column == null || column.typeHandler() == UnknownTypeHandler.class) 181 && !field.getType().isEnum() 182 && !defaultSupportColumnTypes.contains(field.getType())) { 183 continue; 184 } 185 186 187 //列名 188 String columnName = column != null && StringUtil.isNotBlank(column.value()) 189 ? column.value() 190 : (tableInfo.isCamelToUnderline() ? StringUtil.camelToUnderline(field.getName()) : field.getName()); 191 192 //逻辑删除字段 193 if (column != null && column.isLogicDelete()) { 194 if (logicDeleteColumn == null) { 195 logicDeleteColumn = columnName; 196 } else { 197 throw FlexExceptions.wrap("The logic delete column of entity[%s] must be less then 2.", entityClass.getName()); 198 } 199 } 200 201 //乐观锁版本字段 202 if (column != null && column.version()) { 203 if (versionColumn == null) { 204 versionColumn = columnName; 205 } else { 206 throw FlexExceptions.wrap("The version column of entity[%s] must be less then 2.", entityClass.getName()); 207 } 208 } 209 210 //租户ID 字段 211 if (column != null && column.tenantId()) { 212 if (tenantIdColumn == null) { 213 tenantIdColumn = columnName; 214 } else { 215 throw FlexExceptions.wrap("The tenantId column of entity[%s] must be less then 2.", entityClass.getName()); 216 } 217 } 218 219 if (column != null && StringUtil.isNotBlank(column.onInsertValue())) { 220 onInsertColumns.put(columnName, column.onInsertValue().trim()); 221 } 222 223 224 if (column != null && StringUtil.isNotBlank(column.onUpdateValue())) { 225 onUpdateColumns.put(columnName, column.onUpdateValue().trim()); 226 } 227 228 229 if (column != null && column.isLarge()) { 230 largeColumns.add(columnName); 231 } 232 233 234 Id id = field.getAnnotation(Id.class); 235 ColumnInfo columnInfo; 236 if (id != null) { 237 columnInfo = new IdInfo(columnName, field.getName(), field.getType(), id); 238 idInfos.add((IdInfo) columnInfo); 239 } else { 240 columnInfo = new ColumnInfo(); 241 columnInfoList.add(columnInfo); 242 } 243 244 columnInfo.setColumn(columnName); 245 columnInfo.setProperty(field.getName()); 246 columnInfo.setPropertyType(field.getType()); 247 248 if (column != null && column.typeHandler() != UnknownTypeHandler.class) { 249 Class<?> typeHandlerClass = column.typeHandler(); 250 Configuration configuration = FlexGlobalConfig.getDefaultConfig().getConfiguration(); 251 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 252 TypeHandler<?> typeHandler = typeHandlerRegistry.getInstance(columnInfo.getPropertyType(), typeHandlerClass); 253 columnInfo.setTypeHandler(typeHandler); 254 } 255 256 ColumnMask columnMask = field.getAnnotation(ColumnMask.class); 257 if (columnMask != null && StringUtil.isNotBlank(columnMask.value())) { 258 if (String.class != field.getType()) { 259 throw new IllegalStateException("@ColumnMask() only support for string type field. error: " + entityClass.getName() + "." + field.getName()); 260 } 261 columnInfo.setMaskType(columnMask.value().trim()); 262 } 263 264 if (column != null && column.jdbcType() != JdbcType.UNDEFINED) { 265 columnInfo.setJdbcType(column.jdbcType()); 266 } 267 268 if (FlexConsts.DEFAULT_PRIMARY_FIELD.equals(field.getName())) { 269 idField = field; 270 } 271 } 272 273 274 if (idInfos.isEmpty() && idField != null) { 275 int index = -1; 276 for (int i = 0; i < columnInfoList.size(); i++) { 277 ColumnInfo columnInfo = columnInfoList.get(i); 278 if (FlexConsts.DEFAULT_PRIMARY_FIELD.equals(columnInfo.getProperty())) { 279 index = i; 280 break; 281 } 282 } 283 if (index >= 0) { 284 ColumnInfo removedColumnInfo = columnInfoList.remove(index); 285 idInfos.add(new IdInfo(removedColumnInfo)); 286 } 287 } 288 289 tableInfo.setLogicDeleteColumn(logicDeleteColumn); 290 tableInfo.setVersionColumn(versionColumn); 291 tableInfo.setTenantIdColumn(tenantIdColumn); 292 293 if (!onInsertColumns.isEmpty()) { 294 tableInfo.setOnInsertColumns(onInsertColumns); 295 } 296 297 if (!onUpdateColumns.isEmpty()) { 298 tableInfo.setOnUpdateColumns(onUpdateColumns); 299 } 300 301 if (!largeColumns.isEmpty()) { 302 tableInfo.setLargeColumns(largeColumns.toArray(new String[0])); 303 } 304 305 tableInfo.setColumnInfoList(columnInfoList); 306 tableInfo.setPrimaryKeyList(idInfos); 307 308 309 return tableInfo; 310 } 311}