java:通过反射将ResultSet查询结果赋予实体类

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。java:通过反射将ResultSet查询结果赋予实体类,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

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

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!