1 前言
通过java反射,将ResultSet获取结果,通过调用setter方法为实体类赋值,使用方法如下可示。
2 使用
工具类:
StrUtils :
public class StrUtils {private static final String underLineMark = "_";private static final String EMPTY_STRING = "";public static boolean equals(String a, String b){if(null == a){return null == b;}return a.equals(b);}public static int getMatchCount(String str, String subStr){if(!StringUtils.hasLength(str) || !StringUtils.hasLength(subStr)){ExcpUtils.throwExp(MessageFormat.format("getMatchCount's str and subStr should not be null or empty:{0},{1}.",str, subStr));}return StringUtils.countOccurrencesOf(str, subStr);}public static String nonEmptyStr(String value){if(!StringUtils.hasLength(value)){return EMPTY_STRING;}return value;}/*** @param name 小驼峰命名Str* @return 下划线*/@SuppressWarnings("all")public static String humpTransferUnderline(String name){// null or empty throw expExcpUtils.throwExpIfFalse(StringUtils.hasLength(name), "when hump transfer to underline, name should not be empty.");CharSequence cs = name;List charSequenceList = Lists.newArrayList();int temI = 0, i = 0, csLen = 0;for (; i < (csLen = cs.length()); i++) {char c = cs.charAt(i);if(Character.isUpperCase(c)){CharSequence csq = cs.subSequence(temI, i);if(csq.length() > 0){addCharSequence(charSequenceList, csq);temI = i;}}}CharSequence lastSequence = cs.subSequence(temI, csLen);if(lastSequence.length() > 0){addCharSequence(charSequenceList, lastSequence);}// actual could not execute thisif(CollectionUtils.isEmpty(charSequenceList)) return EMPTY_STRING;return String.join(underLineMark, charSequenceList);}private static void addCharSequence(List charSequenceList, CharSequence charSequence) {if(null == charSequenceList){throw new CrawlerForJException("charSequenceList could not be null");}if(null == charSequence || charSequence.length() <= 0){throw new CrawlerForJException("charSequence need non empty");}char[] csqChars = charSequence.toString().toCharArray();char[] initialLowerCsqChar = new char[csqChars.length];initialLowerTransfer(initialLowerCsqChar, csqChars);charSequenceList.add(new String(initialLowerCsqChar));}private static void initialLowerTransfer(char[] targetChar, char[] originChar){ExcpUtils.throwExpIfFalse(ArrayUtils.isNotEmpty(targetChar), "targetChar is empty");ExcpUtils.throwExpIfFalse(ArrayUtils.isNotEmpty(originChar), "originChar is empty");int totalLength;ExcpUtils.throwExpIfFalse((totalLength = originChar.length) == targetChar.length, "targetChar'length not equals to originChar's length");char[] temp;int tempSize;System.arraycopy((temp = new char[]{Character.toLowerCase(originChar[0])}), 0 , targetChar, 0, (tempSize = temp.length));if(totalLength > tempSize){System.arraycopy(originChar, tempSize, targetChar, tempSize, totalLength - tempSize);}}}
ClassUtils:
public class ClassUtils {private static final Map, Class>> primitiveConverter = new ConcurrentHashMap<>(32);private static final Map> primitiveMap = new ConcurrentHashMap<>(32);public static boolean isAssignable(Class> leftClazz, Class> rightClazz){AssertUtils.assertNonNull(leftClazz, "leftClazz should not be null");AssertUtils.assertNonNull(rightClazz, "rightClazz should not be null");return leftClazz.isAssignableFrom(rightClazz);}public static Class> forName(String name){return forName(name, null);}public static Class> forName(String name, @Nullable ClassLoader loader) {ExcpUtils.throwExpIfFalse(null != name, "class name should not be null.");Class> clazz;clazz = resolvePrimitiveType(name);if(clazz != null){return clazz;}ClassLoader cltUse = loader;if(cltUse == null){cltUse = fetchClassLoader();}try {clazz = Class.forName(name, false, cltUse);} catch (ClassNotFoundException notFoundException) {throw new CrawlerForJException("forName raise classNotFound error, name:[" + name + "]." ,notFoundException.getCause());}return clazz;}private static Class> resolvePrimitiveType(String name){Class> clazz = null;if(null != name && name.length() <= 7){clazz = primitiveMap.get(name);}return clazz;}public static ClassLoader fetchClassLoader(){ClassLoader classLoader = null;try {classLoader = Thread.currentThread().getContextClassLoader();}catch (Throwable tx) {// ignore}if(null == classLoader){classLoader = ClassUtils.class.getClassLoader();if(null == classLoader){try {classLoader = ClassLoader.getSystemClassLoader();}catch (Throwable tx) {// ignore}}}return classLoader;}static {primitiveConverter.put(byte.class, Byte.class);primitiveConverter.put(short.class, Short.class);primitiveConverter.put(int.class, Integer.class);primitiveConverter.put(long.class, Long.class);primitiveConverter.put(char.class, Character.class);primitiveConverter.put(boolean.class, Boolean.class);primitiveConverter.put(float.class, Float.class);primitiveConverter.put(double.class, Double.class);primitiveConverter.put(void.class, Void.class);primitiveConverter.forEach((key, value) -> primitiveMap.put(key.getName(), value));}
}
ReflectUtils:
public class ReflectUtils {private static final String pkgSeparator = ".";private static final String ToString = "toString";private static final String HashCode = "hashCode";private static final String Equals = "equals";private static final Object[] EMPTY_ARGUMENT = new Object[0];public static boolean isToStringMethod(Method method){if(null == method){return false;}return method.getName().equals(ToString) && method.getParameterCount() == 0&& method.getReturnType().equals(String.class);}public static boolean isHashCodeMethod(Method method){if(null == method){return false;}return method.getName().equals(HashCode) && method.getParameterCount() == 0&& method.getReturnType().isPrimitive();}public static boolean isEqualsMethod(Method method){if(null == method){return false;}return method.getName().equals(Equals) && method.getParameterCount() == 1&& method.getParameterTypes()[0] == Object.class;}@SuppressWarnings("all")public static Object[] adaptArgumentsIfNecessary(Method method, Object[] args){int length;if(args == null || (length = args.length) == 0){return EMPTY_ARGUMENT;}// method parameter contains such as run(Object obj, String... strings), like stringsif(null != method && method.isVarArgs()){if(method.getParameterCount() == length){// Vararg parameter must be the last in the list// and only oneint lastVarargIndex;Class> varargType = method.getParameterTypes()[lastVarargIndex = length - 1];if(varargType.isArray()){Object vararg = args[lastVarargIndex];// the last Object vararg type not match varargType// maybe the varargType is String[] class, but vararg is new Integer[0]if(vararg instanceof Object[] && !varargType.isInstance(vararg)){Object[] newArgs = new Object[length];System.arraycopy(args, 0, newArgs, 0, lastVarargIndex);Class> targetComponentType = varargType.getComponentType();int varargLen = Array.getLength(vararg);Object varargArray = Array.newInstance(targetComponentType, varargLen);System.arraycopy(vararg, 0, varargArray, 0, varargLen);newArgs[lastVarargIndex] = varargArray;return newArgs;}}}}return args;}public static Object invokeAvailableExecute(Object target, Method method, Object[] args){makeMethodAccessible(method);return invokeExecute(target, method, args);}public static Object getObject(Class> clazz){AssertUtils.assertNonNull(clazz, "getObject's clazz is null");Object obj;try {// when clazz not in same Package, and maybe it's protected or private// there is something wrong// access errorassertClassAccessible(clazz);//no such method errorisNoArgsConstructorExist(clazz);obj = clazz.newInstance();}catch (InstantiationException | IllegalAccessException e) {throw new IllegalStateException("could not fetch Object from class:[" + clazz + "].");}return obj;}private static void isNoArgsConstructorExist(Class> clazz) {boolean isNoArgsConsExist = false;for (Constructor> dclConstructor : clazz.getDeclaredConstructors()) {if(dclConstructor.getParameterCount() == 0 &&ArrayUtils.isEmpty(dclConstructor.getParameterTypes())){isNoArgsConsExist = true;break;}}AssertUtils.assertTrue(isNoArgsConsExist, "no args constructor do not exists, Class["+ clazz.getPackage().getName() + pkgSeparator + clazz.getSimpleName() + "].");}public static Object invokeExecute(Object target, Method method, Object[] args){try {return method.invoke(target, args);} catch (Exception e) {handleReflectException(e);} catch (Throwable tx){throwExceptionWithMessage(tx, "invoke unknown error occurred, " + tx.getMessage());}throw new CrawlerForJException("could not reach here");}public static void assertClassAccessible(Class> clazz){AssertUtils.assertNonNull(clazz, "try to access clazz, but it's null");if(!Modifier.isPublic(clazz.getModifiers())){throw new CrawlerForJException("getObject's object must be public, Class[" +clazz.getPackage().getName() + pkgSeparator + clazz.getSimpleName() +"].");}}public static void makeMethodAccessible(Method method){AssertUtils.assertNonNull(method, "try to access method, but it's null");if(!method.isAccessible() &&(!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))){method.setAccessible(true);}}private static void handleReflectException(Exception ex){if(ex instanceof NoSuchMethodException){throw new IllegalStateException("no such method find, " + ex.getMessage(), ex);}if(ex instanceof IllegalArgumentException){throw (IllegalArgumentException)ex;}if(ex instanceof IllegalAccessException){throw new IllegalStateException("could not access method or field, " + ex.getMessage(), ex);}if(ex instanceof InvocationTargetException){throwException(((InvocationTargetException) ex).getTargetException());}}private static void throwExceptionWithMessage(Throwable tx, String message){if(StringUtils.hasLength(message)){if(tx instanceof RuntimeException || tx instanceof Error){throw new CrawlerForJException(message, tx);}throw new UndeclaredThrowableException(tx, message);}else{throwException(tx);}}public static void throwException(Throwable tx){if(tx instanceof RuntimeException){throw (RuntimeException)tx;}if(tx instanceof Error){throw (Error)tx;}throw new UndeclaredThrowableException(tx);}public static Field[] getAllDeclaredFields(Class> clazz){AssertUtils.assertNonNull(clazz, "clazz access null");Field[] allField = null;do{Field[] declaredFields = clazz.getDeclaredFields();allField = ArrayUtils.addAll(allField, declaredFields);// no Object class fields, do not need judgeclazz = clazz.getSuperclass();}while(clazz != null);return allField;}public static Method invokeFetchWriteMethod(String name, Class> clazz){AssertUtils.assertNonEmpty(name, "name access null or empty");AssertUtils.assertNonNull(clazz, "clazz access null");Method writeMethod;try {PropertyDescriptor descriptor = new PropertyDescriptor(name, clazz);writeMethod = descriptor.getWriteMethod();} catch (IntrospectionException e) {throw new CrawlerForJException(e);}if(null == writeMethod){throw new CrawlerForJException("can not find write method, name:[" + name+ "]; " + "class:[" + clazz + "].");}return writeMethod;}}
ExcpUtils:
public class ExcpUtils {/* 不为true则抛出异常 */public static void throwExpIfFalse(boolean result,String msg){if(StringUtils.hasLength(msg)&&!result){throw new CrawlerForJException(msg);}else if(!StringUtils.hasLength(msg)){throw new AccessParamException(String.format("调用throwExpIfFalse方法的msg不能为空:%s",msg));}}/* 抛出异常的工具方法 */public static void throwExp(String message){if(StringUtils.hasLength(message)){throw new CrawlerForJException(message);}else{throw new AccessParamException(String.format("方法%s的参数不能为空:%s",ExcpUtils.class.getSimpleName()+Thread.currentThread().getStackTrace()[1].getMethodName(),message));}}
}
AssertUtils :
public class AssertUtils {/* 校验为真 */public static void assertTrue(boolean res, String errorMsg){handlerError(res, errorMsg);}/* 校验非空 */public static void assertNonNull(T obj, String errorMsg){handlerError(null != obj, errorMsg);}/* 校验非null非empty字符串 */public static void assertNonEmpty(String str, String errorMsg){handlerError(null != str && !str.isEmpty(), errorMsg);}/* 校验非空Array */public static void assertNonEmptyArray(T[] array, String errorMsg){handlerError(!ArrayUtils.isEmpty(array), errorMsg);}/* 统一异常处理 */private static void handlerError(boolean flag, String message){if(!flag){/* 使用公共异常处理 */throw new CrawlerForJException(message);}}
}
具体实现如下:
TypeConverterRegistry :
public class TypeConverterRegistry {private static final Map, TypeConverter>> typeConverterMap;public static void registry(Class javaType, TypeConverter typeConverter){// ConcurrentHashMap'key and value all not allow nullAssertUtils.assertNonNull(javaType, "javaType access null");AssertUtils.assertNonNull(typeConverter, "typeConverter access null");typeConverterMap.put(javaType, typeConverter);}public TypeConverter> get(Class> javaType){return this.getOrDefault(javaType, null);}public TypeConverter> getOrDefault(Class> javaType,TypeConverter> newTypeConverter){AssertUtils.assertNonNull(javaType, "typeConverterMap's key should not be null");TypeConverter> tConvert = typeConverterMap.getOrDefault(javaType, newTypeConverter);if(null != tConvert){return tConvert;}throw new CrawlerForJException("TypeConverterRegistry's converter fetch null");}interface TypeConverter{T getNullableResult(ResultSet rs, int colIndex) throws SQLException;}static {typeConverterMap = new ConcurrentHashMap<>();// mysql varchar char typeTypeConverterRegistry.registry(String.class, ResultSet::getString);TypeConverterRegistry.registry(Short.class, (rs, colIndex) -> {short shortRes;return (shortRes = rs.getShort(colIndex)) == 0 && rs.wasNull() ? null : shortRes;});// mysql smallint tinyint int typeTypeConverterRegistry.registry(Integer.class, ((rs, colIndex) -> {int intRes;return (intRes = rs.getInt(colIndex)) == 0 && rs.wasNull() ? null : intRes;}));// mysql bigint typeTypeConverterRegistry.registry(Long.class, (rs, colIndex) -> {long longRes;return (longRes = rs.getLong(colIndex)) == 0 && rs.wasNull() ? null : longRes;});// mysql datetime typeTypeConverterRegistry.registry(LocalDateTime.class, (rs, colIndex) ->rs.getObject(colIndex, LocalDateTime.class));// mysql date typeTypeConverterRegistry.registry(java.sql.Date.class, ResultSet::getDate);// mysql timestamp typeTypeConverterRegistry.registry(java.sql.Timestamp.class, ResultSet::getTimestamp);// mysql time typeTypeConverterRegistry.registry(java.sql.Time.class, ResultSet::getTime);// mysql decimal typeTypeConverterRegistry.registry(BigDecimal.class, ResultSet::getBigDecimal);// mysql float typeTypeConverterRegistry.registry(Float.class, (rs, colIndex) -> {float floatRes;return (floatRes = rs.getFloat(colIndex)) == 0.0F && rs.wasNull()? null : floatRes;});// mysql double typeTypeConverterRegistry.registry(Double.class, (rs, colIndex) -> {double doubleRes;return (doubleRes = rs.getDouble(colIndex)) == 0.0D && rs.wasNull()? null : doubleRes;});}
}
反射元信息类:
public interface FieldMetaInfoWrap {Method getFieldWriteMethod();String getFieldName();
}
@SuppressWarnings("all")
public class FieldMetaInfo implements FieldMetaInfoWrap {private static final String emptyString = "";private Class> clazz;private Field field;private String FieldName;private volatile Method fieldWriteMethod;private Object dbValue;public FieldMetaInfo(Class> clazz, Field field) {AssertUtils.assertNonNull(clazz, "FieldMetaInfo'class not allow null");AssertUtils.assertNonNull(field, "FieldMetaInfo'field not allow null");this.clazz = clazz;this.field = field;}public Class> getClazz() {return clazz;}public void setClazz(Class> clazz) {this.clazz = clazz;}public Field getField() {return field;}public void setField(Field field) {this.field = field;}public void setFieldName(String fieldName) {FieldName = fieldName;}public void setFieldWriteMethod(Method fieldWriteMethod) {this.fieldWriteMethod = fieldWriteMethod;}@Overridepublic Method getFieldWriteMethod() {if(null == this.fieldWriteMethod){this.fieldWriteMethod = ReflectUtils.invokeFetchWriteMethod(this.getFieldName(), this.getClazz());}return this.fieldWriteMethod;}@Overridepublic String getFieldName() {if(this.field != null){return this.field.getName();}return emptyString;}public Object getDbValue() {return dbValue;}public void setDbValue(Object dbValue) {this.dbValue = dbValue;}
}
public class ClassMetaInfo {Class> ownerClazz;FieldMetaInfo[] fieldMetaInfoList;public ClassMetaInfo(Class> ownerClazz, FieldMetaInfo[] fieldMetaInfoList) {classMetaInfoCheck(ownerClazz, fieldMetaInfoList);this.ownerClazz = ownerClazz;this.fieldMetaInfoList = fieldMetaInfoList;}private void classMetaInfoCheck(Class> ownerClazz, FieldMetaInfo[] fieldMetaInfoList) {AssertUtils.assertNonNull(ownerClazz, "ownerClazz access null");AssertUtils.assertNonEmptyArray(fieldMetaInfoList, "fieldMetaInfoList access null or empty");Set extends Class>> clazzSet = Arrays.stream(fieldMetaInfoList).map(FieldMetaInfo::getClazz).collect(Collectors.toSet());AssertUtils.assertTrue(clazzSet.size() == 1, "ClassMetaInfo's fieldMetaInfoList must have one clazz");AssertUtils.assertTrue(clazzSet.contains(ownerClazz), "clazzSet do not contains ownerClazz:[" + ownerClazz + "].");}public Class> getOwnerClazz() {return ownerClazz;}public void setOwnerClazz(Class> ownerClazz) {this.ownerClazz = ownerClazz;}public FieldMetaInfo[] getFieldMetaInfoList() {return fieldMetaInfoList;}public void setFieldMetaInfoList(FieldMetaInfo[] fieldMetaInfoList) {this.fieldMetaInfoList = fieldMetaInfoList;}
}
业务逻辑实现类(单测执行):
public class TestResultSet extends AbstractBaseTest {private DataSource source;private PreparedStatement preparedStatement;private ResultSet resultSet;private Connection connection;private static final Object dataSourceMonitor = new Object();private static final Object dbMethodMonitor = new Object();private static final Map cachedDataSource;private static final Map expireTimeMap;private static final Map cachedClazzMethodMapping;// default expire time : three hoursprivate static final Long DEFAULT_EXPIRE_TIME = 1000L * 60 * 60 * 3;private static final ScheduledExecutorService scheduledThreadPoolExecutor;private static final Executor threadPoolExecutor;private TypeConverterRegistry typeRegistry;private static final String IllegalParamTypeErrorStackTrace = "argument type mismatch";private static final Map>> dbNameMapping;private static final Object dbNameMappingMonitor = new Object();private static final Logger logger = LoggerFactory.getLogger(TestResultSet.class);@Beforepublic void beforeLoad(){// 5 sec later, dataSource expirethis.source = getCachedDataSource("fruit", 5000L);try {((DruidDataSource)this.source).init();} catch (SQLException throwables) {ExcpUtils.throwExp("init fail:" + throwables.getCause());}typeRegistry = new TypeConverterRegistry();}private DataSource initDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setInitialSize(1);dataSource.setMinIdle(0);dataSource.setMaxActive(4);// 配置获取连接超时时间(-1表示可以一直等待)dataSource.setMaxWait(5000);// 连接保持空闲而不被驱逐的最小时间: 5分钟dataSource.setMinEvictableIdleTimeMillis(1000L * 60 * 5);// 连接保持空闲而不被驱逐的最大时间: 2小时dataSource.setMaxEvictableIdleTimeMillis(1000L * 60 * 120);dataSource.setUrl("jdbc:mysql://localhost:3306/fruitmall?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai");dataSource.setUsername("root");dataSource.setPassword("******");return dataSource;}@Testpublic void test_resultSet() throws Exception{try {this.connection = this.source.getConnection();this.preparedStatement = this.connection.prepareStatement("select * from my_fruit");this.resultSet = this.preparedStatement.executeQuery();ResultSetMetaData metaData = resultSet.getMetaData();cachedDBMethodMappingFetchByClazz("my_fruit", FruitDto.class);List fruitDtos = Lists.newArrayList();while (resultSet.next()){resultSetRDataAndMetaDataTransfer(resultSet,metaData,"my_fruit",fruitDtos);}System.out.println("数据库结果:");fruitDtos.forEach(System.out::println);} catch (SQLException th) {throw new CrawlerForJException("sql Exception occured", th.getCause());} finally {closeAll();// per execute, async remove expiredexecuteFinalPollingCheck();}}@SuppressWarnings("unchecked")public void resultSetRDataAndMetaDataTransfer(ResultSet resultSet,ResultSetMetaData metaData,String tableName,List dataList){AssertUtils.assertNonNull(metaData, "metaData access null");AssertUtils.assertNonEmpty(tableName, "tableName access null or empty");boolean fresh = false;try {// cached ClassMetaInfo fetchClassMetaInfo classMetaInfo = cachedDBMethodMappingFetch(tableName);AssertUtils.assertNonNull(classMetaInfo, "classMetaInfo access null");int i = 1;FieldMetaInfo[] fieldMetaInfoList = classMetaInfo.getFieldMetaInfoList();Object target = ReflectUtils.getObject(classMetaInfo.getOwnerClazz());Method setterM;Map> dbTable;if(CollectionUtils.isEmpty(dbTable = getDBNameMap(tableName))){dbTable = initialDBNameMap(tableName);fresh = true;}do{String dbUnderLine = StrUtils.nonEmptyStr(metaData.getColumnName(i));Class> dbMetaClass;
/*if(fresh){dbMetaClass =ClassUtils.forName(metaData.getColumnClassName(i));dbTable.put(dbUnderLine, dbMetaClass);}else{dbMetaClass = dbTable.get(dbUnderLine);}
*/dbMetaClass = fresh? (dbTable.put(dbUnderLine,(dbMetaClass = ClassUtils.forName(metaData.getColumnClassName(i)))) == null? dbMetaClass: dbMetaClass): dbTable.get(dbUnderLine);List matchFieldMetaInfo = Arrays.stream(fieldMetaInfoList).filter(fieldMetaInfo -> dbUnderLine.equals(StrUtils.humpTransferUnderline(fieldMetaInfo.getFieldName()))).collect(Collectors.toList());if(CollectionUtils.isEmpty(matchFieldMetaInfo)){continue;}TypeConverterRegistry.TypeConverter> typeConverter= this.typeRegistry.get(dbMetaClass);if(matchFieldMetaInfo.size() == 1){FieldMetaInfo fieldMetaInfo = matchFieldMetaInfo.get(0);Object nullableResult = typeConverter.getNullableResult(resultSet, i);Field singleField = fieldMetaInfo.getField();if(isNullableResultPrimitive(singleField, nullableResult)){// when type is primitive,and value is null, just continue// because wrapper class to primitive type,do not allow null valuecontinue;}setterM = fieldMetaInfo.getFieldWriteMethod();fillProps(target, setterM, nullableResult);}if(matchFieldMetaInfo.size() > 1){// such as field like singleName and single_name,// or a and A...// this will more than 1 (2 or more)Field multiField;label326 : {for (FieldMetaInfo fieldMetaInfo : matchFieldMetaInfo) {if((multiField = fieldMetaInfo.getField()) != null&& multiField.getType().equals(dbMetaClass)){setterM = fieldMetaInfo.getFieldWriteMethod();break label326;}}throw new CrawlerForJException("multiple fieldMeta math, setter method access null");}Object nullableResult = typeConverter.getNullableResult(resultSet, i);if(isNullableResultPrimitive(multiField, nullableResult)){// when type is primitive,and value is null, just continue// because wrapper class to primitive type,do not allow null valuecontinue;}fillProps(target, setterM, nullableResult);}}while(++i < metaData.getColumnCount() + 1);dataList.add((T)target);} catch (SQLException sqlException) {throw new CrawlerForJException(sqlException);}}private void fillProps (Object target, Method setterMethod, Object targetArg){try {ReflectUtils.invokeAvailableExecute(target,setterMethod,new Object[]{targetArg});} catch (IllegalArgumentException illegalArgumentException) {if(isArgsTypeNotMatch(illegalArgumentException)){// ignore expLogUtils.warn(logger,"param type error, ignore this setter:[{}]",setterMethod.getName());}else if(!StringUtils.hasLength(illegalArgumentException.getMessage())){//ignoreLogUtils.warn(logger,"illegalArgumentException error with no msg, " +"ignore this setter:[{}]",setterMethod.getName());}else if(StringUtils.hasLength(illegalArgumentException.getMessage())&& illegalArgumentException.getMessage().startsWith("java.lang.ClassCastException")){LogUtils.warn(logger,"illegalArgumentException error with ClassCastException," +"ignore this setter:[{}]",setterMethod.getName());}else {throw illegalArgumentException;}}}private Map> initialDBNameMap(String tableName){Map> dbTableMap = dbNameMapping.get(tableName);if(null == dbTableMap){synchronized (dbNameMappingMonitor) {dbTableMap = dbNameMapping.get(tableName);if(null == dbTableMap){dbTableMap = dbNameMapping.computeIfAbsent(tableName, dbNameKey -> new HashMap<>());}}}return dbTableMap;}private static Map> getDBNameMap(String tableName){AssertUtils.assertNonEmpty(tableName,"getDBNameMap's tableName access empty");return dbNameMapping.get(tableName);}private boolean isArgsTypeNotMatch(IllegalArgumentException illegalArgumentException) {String msg;if(illegalArgumentException != null){return (msg = illegalArgumentException.getMessage()) != null&& msg.startsWith(IllegalParamTypeErrorStackTrace);}throw new CrawlerForJException("illegalArgumentException access null");}private boolean isNullableResultPrimitive(Field field, Object nullableResult){AssertUtils.assertNonNull(field,"isNullableResultPrimitive's field access null");// when type is primitive,and value is null, just continue// because wrapper class to primitive type,do not allow null valuereturn null == nullableResult && field.getType().isPrimitive();}private ClassMetaInfo cachedDBMethodMappingFetch(String tableName){return cachedDBMethodMappingFetchByClazz(tableName, null);}private ClassMetaInfo cachedDBMethodMappingFetchByClazz(String tableName,@Nullable Class> clazz) {ClassMetaInfo dbFieldMapping = getDBFieldMapping(tableName);if(null == dbFieldMapping){synchronized (dbMethodMonitor){dbFieldMapping = getDBFieldMapping(tableName);if(null == dbFieldMapping){if(null != clazz){putDBFieldMapping(tableName, clazz);dbFieldMapping = getDBFieldMapping(tableName);}}}}return dbFieldMapping;}private void putDBFieldMapping(String tableName, @NonNull Class> clazz) {AssertUtils.assertNonEmpty(tableName, "tableName access empty");AssertUtils.assertNonNull(clazz, "clazz access null");Field[] allDeclaredFields = ReflectUtils.getAllDeclaredFields(clazz);//empty fields, will throw expClassMetaInfo classMetaInfo = new ClassMetaInfo(clazz,Arrays.stream(allDeclaredFields).map(field -> new FieldMetaInfo(clazz, field)).toArray(FieldMetaInfo[]::new));// key tableName; value fieldMappingMethodcachedClazzMethodMapping.put(tableName, classMetaInfo);}private ClassMetaInfo getDBFieldMapping(String tableName){AssertUtils.assertNonEmpty(tableName, "tableName access empty");return cachedClazzMethodMapping.get(tableName);}private DataSource getCachedDataSource(String key, Long expireMilliSecondTime){if(null != expireMilliSecondTime){ExcpUtils.throwExpIfFalse(expireMilliSecondTime > 0 ,"expireMilliSecondTime should more over than 0");}ExcpUtils.throwExpIfFalse(StringUtils.hasLength(key),"key should has length");// remove expiredexpireDoCheck(key);DataSource cachedSource;cachedSource = cachedDataSource.get(key);if(cachedSource == null || ((DruidDataSource)cachedSource).isClosed()){synchronized(dataSourceMonitor){cachedSource = cachedDataSource.get(key);if(null == cachedSource ||((DruidDataSource)cachedSource).isClosed()){if(cachedSource != null&& ((DruidDataSource)cachedSource).isClosed()){// remove closedremoveAll(key);}cachedDataSource.put(key,(cachedSource = initDataSource()));if(null == expireMilliSecondTime){expireTimeMap.put(key,System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);}else{expireTimeMap.put(key,System.currentTimeMillis() + expireMilliSecondTime);}}}}return cachedSource;}private static void pollingExpire(){Iterator> iterator;for (iterator = expireTimeMap.entrySet().iterator(); iterator.hasNext(); ) {Map.Entry nextExpire = iterator.next();String key;if((key = nextExpire.getKey()) != null){// key should not be nullexpireDoCheck(key);}}}private static void expireDoCheck(String key){// key should not be nullexpireTimeMap.forEach((key1, value) -> {if (key.equals(key1)) {if (value != null && value < System.currentTimeMillis()) {removeAll(key);}}});}private static void scheduledPollingCheck(){scheduledThreadPoolExecutor.scheduleWithFixedDelay(TestResultSet::pollingExpire,1000L,DEFAULT_EXPIRE_TIME,TimeUnit.MILLISECONDS);}private static void executeFinalPollingCheck(){threadPoolExecutor.execute(TestResultSet::pollingExpire);}private static void removeAll(String key){cachedDataSource.remove(key);expireTimeMap.remove(key);}private void closeAll(){try {if(this.connection != null && !this.connection.isClosed()){this.connection.close();}if(this.preparedStatement != null &&!this.preparedStatement.isClosed()){this.preparedStatement.close();}if(this.resultSet != null && !this.resultSet.isClosed()){this.resultSet.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}static {//lazy initcachedDataSource = new ConcurrentHashMap<>(1024);expireTimeMap = new ConcurrentHashMap<>(1024);cachedClazzMethodMapping = new ConcurrentHashMap<>(64);dbNameMapping = new ConcurrentHashMap<>(128);scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);threadPoolExecutor = new ThreadPoolExecutor(2, 4,10,TimeUnit.SECONDS,new LinkedBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());scheduledPollingCheck();}}
数据库实体类:
@Data
@ToString
public class FruitDto {long fruitId;LocalDateTime createTime;Date modifyTime;String extraInfo;BigDecimal crossOutPrice;long fruitStock;String name;Long sales;Integer supId;double unitPrice;long unitWeight;Date fruitDate;Date fruitGmt;Date fruitTime;Short fruitSmall;byte fruitTiny;float number;Double dNumber;float d_number;Object data;Object mark;
}
执行单测test_resultSet,结果如下,可见resultSet的结果,通过反射直接转换为了实体类:
数据库数据如下: