java:通过反射将ResultSet查询结果赋予实体类
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 exp
ExcpUtils.throwExpIfFalse(StringUtils.hasLength(name), "when hump transfer to underline, name should not be empty.");
CharSequence cs = name;
List<CharSequence> 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 this
if(CollectionUtils.isEmpty(charSequenceList)) return EMPTY_STRING;
return String.join(underLineMark, charSequenceList);
}
private static void addCharSequence(List<CharSequence> 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<?>, Class<?>> primitiveConverter = new ConcurrentHashMap<>(32);
private static final Map<String, Class<?>> 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 strings
if(null != method && method.isVarArgs()){
if(method.getParameterCount() == length){
// Vararg parameter must be the last in the list
// and only one
int 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 error
assertClassAccessible(clazz);
//no such method error
isNoArgsConstructorExist(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 judge
clazz = 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 <T> void assertNonNull(T obj, String errorMsg){
handlerError(null != obj, errorMsg);
}
/* 校验非null非empty字符串 */
public static <T> void assertNonEmpty(String str, String errorMsg){
handlerError(null != str && !str.isEmpty(), errorMsg);
}
/* 校验非空Array */
public static <T> 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<Class<?>, TypeConverter<?>> typeConverterMap;
public static <T> void registry(Class<T> javaType, TypeConverter<T> typeConverter){
// ConcurrentHashMap'key and value all not allow null
AssertUtils.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>{
T getNullableResult(ResultSet rs, int colIndex) throws SQLException;
}
static {
typeConverterMap = new ConcurrentHashMap<>();
// mysql varchar char type
TypeConverterRegistry.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 type
TypeConverterRegistry.registry(Integer.class, ((rs, colIndex) -> {
int intRes;
return (intRes = rs.getInt(colIndex)) == 0 && rs.wasNull() ? null : intRes;
}));
// mysql bigint type
TypeConverterRegistry.registry(Long.class, (rs, colIndex) -> {
long longRes;
return (longRes = rs.getLong(colIndex)) == 0 && rs.wasNull() ? null : longRes;
});
// mysql datetime type
TypeConverterRegistry.registry(LocalDateTime.class, (rs, colIndex) ->
rs.getObject(colIndex, LocalDateTime.class));
// mysql date type
TypeConverterRegistry.registry(java.sql.Date.class, ResultSet::getDate);
// mysql timestamp type
TypeConverterRegistry.registry(java.sql.Timestamp.class, ResultSet::getTimestamp);
// mysql time type
TypeConverterRegistry.registry(java.sql.Time.class, ResultSet::getTime);
// mysql decimal type
TypeConverterRegistry.registry(BigDecimal.class, ResultSet::getBigDecimal);
// mysql float type
TypeConverterRegistry.registry(Float.class, (rs, colIndex) -> {
float floatRes;
return (floatRes = rs.getFloat(colIndex)) == 0.0F && rs.wasNull()
? null : floatRes;
});
// mysql double type
TypeConverterRegistry.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;
}
@Override
public Method getFieldWriteMethod() {
if(null == this.fieldWriteMethod){
this.fieldWriteMethod = ReflectUtils
.invokeFetchWriteMethod(this.getFieldName(), this.getClazz());
}
return this.fieldWriteMethod;
}
@Override
public 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<String, DataSource> cachedDataSource;
private static final Map<String, Long> expireTimeMap;
private static final Map<String, ClassMetaInfo> cachedClazzMethodMapping;
// default expire time : three hours
private 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<String, Map<String, Class<?>>> dbNameMapping;
private static final Object dbNameMappingMonitor = new Object();
private static final Logger logger = LoggerFactory.getLogger(TestResultSet.class);
@Before
public void beforeLoad(){
// 5 sec later, dataSource expire
this.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;
}
@Test
public 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<FruitDto> 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 expired
executeFinalPollingCheck();
}
}
@SuppressWarnings("unchecked")
public <T> void resultSetRDataAndMetaDataTransfer(ResultSet resultSet,
ResultSetMetaData metaData,
String tableName,
List<T> dataList){
AssertUtils.assertNonNull(metaData, "metaData access null");
AssertUtils.assertNonEmpty(tableName, "tableName access null or empty");
boolean fresh = false;
try {
// cached ClassMetaInfo fetch
ClassMetaInfo 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<String, Class<?>> 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<FieldMetaInfo> 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 value
continue;
}
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 value
continue;
}
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 exp
LogUtils.warn(logger,
"param type error, ignore this setter:[{}]",
setterMethod.getName());
}
else if(!StringUtils.hasLength(illegalArgumentException.getMessage())){
//ignore
LogUtils.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<String, Class<?>> initialDBNameMap(String tableName){
Map<String, Class<?>> 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<String, Class<?>> 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 value
return 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 exp
ClassMetaInfo classMetaInfo = new ClassMetaInfo(
clazz,
Arrays.stream(allDeclaredFields)
.map(field -> new FieldMetaInfo(clazz, field))
.toArray(FieldMetaInfo[]::new));
// key tableName; value fieldMappingMethod
cachedClazzMethodMapping.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 expired
expireDoCheck(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 closed
removeAll(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<Map.Entry<String, Long>> iterator;
for (iterator = expireTimeMap.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<String, Long> nextExpire = iterator.next();
String key;
if((key = nextExpire.getKey()) != null){
// key should not be null
expireDoCheck(key);
}
}
}
private static void expireDoCheck(String key){
// key should not be null
expireTimeMap.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 init
cachedDataSource = 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的结果,通过反射直接转换为了实体类:
数据库数据如下:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/192084.html