All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mario6.common.db.mapper.ClassInfo Maven / Gradle / Ivy

The newest version!
package com.mario6.common.db.mapper;

import com.mario6.common.db.util.Utils;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 存储类的反射得到的一些信息
 */
public class ClassInfo {

    private ClassOnlyInfo classInfo;
    private FieldInfo idFieldInfo;
    private List otherFieldsInfo;

    private static ConcurrentHashMap cache = new ConcurrentHashMap<>();

    private ClassInfo(Class clazz, boolean fastFailed) {
        this.classInfo = new ClassOnlyInfo(clazz);
        Field[] fields = clazz.getDeclaredFields();
        this.otherFieldsInfo = new ArrayList<>(fields.length);
        boolean isId = false;
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            int modifiers = field.getModifiers();
            if( Modifier.isStatic(modifiers) ) {
                // 忽略静态字段
                continue;
            }
            // TODO 可能需要考虑Id注解重复的情况
            Id annotation = field.getAnnotation(Id.class);
            if(annotation != null && !isId) {
                this.setIdField(field);
                isId = true;
            } else {
                this.addOtherFields(field);
            }
        }
        if(!isId && fastFailed) {
            throw new IllegalArgumentException("实体类未标示@Id主键字段 " + clazz.getName());
        }
    }

    /**
     * 静态工厂方法
     */
    public static  ClassInfo newInstance(Class clazz, boolean fastFailed) {
        ClassInfo classInfo = cache.get(clazz);
        // TODO 修复可能出现的多线程重复计算问题
        if(classInfo == null) {
            classInfo = new ClassInfo(clazz, fastFailed);
            cache.put(clazz, classInfo);
        } else if(fastFailed == true) {
            if(classInfo.idFieldInfo == null) {
                throw new IllegalStateException("Id字段未标示");
            }
        }
        return classInfo;
    }

    public static  ClassInfo newInstance(Class clazz) {
        return newInstance(clazz, true);
    }


    /**
     * 获得insert语句的列列表
     * @param snake 蛇形命名
     * @return
     */
    public List getInsertColumns(boolean snake) {
        return doGetInsertColumns(null, false, snake);
    }

    /**
     * 获得insert语句的列列表, 对null敏感
     * @param snake 蛇形命名
     * @return
     */
    public List getInsertColumnsSelective(T obj, boolean snake) {
        return doGetInsertColumns(obj, true, snake);
    }


    /**
     * 获得insert语句列列表
     * @param obj 该类型对象,当切仅当selective==false时可以为null
     * @param selective 空值不插入
     * @param snake 字段蛇形命名
     * @return
     */
    private List doGetInsertColumns(T obj, boolean selective, boolean snake) {
        ArrayList results = new ArrayList<>(this.otherFieldsInfo.size());
        List otherFieldsInfo = this.otherFieldsInfo;
        for(int i =0; i getInsertValues(T obj) {
        return doGetInsertValues(obj, false);
    }

    /**
     * 获得insert语句的列的值,对null敏感
     * @param obj
     * @return
     */
    public List getInsertValuesSelective(T obj) {
        return doGetInsertValues(obj, true);
    }

    /**
     * 获得insert语句的列的值
     * @param obj 对象
     * @param selective null值敏感
     * @return
     */
    private List doGetInsertValues(T obj, boolean selective) {
        ArrayList results = new ArrayList<>(this.otherFieldsInfo.size() + 1);
        List otherFieldsInfo = this.otherFieldsInfo;
        for(int i =0; i setList = new ArrayList<>(otherFieldsInfo.size());
        for(FieldInfo field: otherFieldsInfo) {
            if(field.isTransient()
                    || (selective && field.getValue(obj) == null)) {
                continue;
            }

            String key = field.getName(snake);
            String set = key + "=?";
            setList.add(set);
        }
        return Utils.join(",", setList);
    }


    /**
     * 获得update语句所需要的插入值
     * @param obj
     * @return
     */
    public List getUpdateSetValues(T obj) {
        return doGetUpdateSetValues(obj, false);
    }

    /**
     * 呼的update语句所玉箫的插入值,对null敏感
     * @param obj
     * @return
     */
    public List getUpdateSetValuesSelective(T obj) {
        return doGetUpdateSetValues(obj, true);
    }

    /**
     * 获得Update语句的值
     * @param obj
     * @return
     */
    private List doGetUpdateSetValues(T obj, boolean selective) {
        List results = new ArrayList<>(otherFieldsInfo.size());
        for(FieldInfo field: otherFieldsInfo) {
            if(field.isTransient()
                    || (selective && field.getValue(obj) == null)) {
                continue;
            }
            Object value = field.getValue(obj);
            results.add(value);
        }
        return results;
    }

    /**
     * 获得所有字段的信息
     * @return
     */
    public List getFieldsInfo() {
        List results = new ArrayList<>(otherFieldsInfo);
        results.add(idFieldInfo);
        return results;
    }

    /**
     * 创建该类型的一个实例,通过无参构造函数
     * @return
     */
    public T createByDefaultConstructor() {
        Class clazz = classInfo.getClazz();
        T obj = null;
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor constructor: constructors) {
            constructor.setAccessible(true);
            int count = constructor.getParameterTypes().length;
            if(count == 0) {
                try {
                    obj = (T) constructor.newInstance();
                    break;
                } catch (InstantiationException e) {
                    throw new IllegalStateException("对应实体类型没有无参构造函数" + clazz.getName(), e);
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException("对应实体类型没有无参构造函数" + clazz.getName(), e);
                } catch (InvocationTargetException e) {
                    throw new IllegalStateException("对应实体类型没有无参构造函数" + clazz.getName(), e);
                }
            }
        }
        if(obj == null) {
            throw new IllegalStateException("没有无参构造方法" + clazz.getName());
        }
        return obj;
    }

    //--------------------------------------------------------------------------
    // 辅助函数
    //--------------------------------------------------------------------------
    /**
     * 设置主键信息
     * @param id
     */
    private void setIdField(Field id) {
        this.idFieldInfo = new FieldInfo(id);
    }

    /**
     * 添加其他字段信息
     * @param other
     */
    private void addOtherFields(Field other) {
        FieldInfo fieldInfo = new FieldInfo(other);
        this.otherFieldsInfo.add(fieldInfo);
    }

    public Class getIdType() {
        return idFieldInfo.getType();
    }


    /**
     * 类相关的部分信息
     */
    class ClassOnlyInfo {
        private Class clazz;
        private String className;
        private String snakeClassName;
        private String nameInTable;

        public ClassOnlyInfo(Class clazz) {
            this.clazz = clazz;
            doExtractClass(clazz);
        }

        private void doExtractClass(Class clazz) {
            this.className = clazz.getSimpleName();
            this.snakeClassName = Utils.camelToSnake(this.className);
            Table tableAnnotation = clazz.getAnnotation(Table.class);
            if(tableAnnotation != null) {
                this.nameInTable = tableAnnotation.name();
            }
        }

        public Class getClazz() {
            return clazz;
        }

        public String getClassName() {
            return className;
        }

        public String getSnakeClassName() {
            return snakeClassName;
        }

        public String getNameInTable() {
            return nameInTable;
        }
    }

    /**
     * 字段信息
     */
    public class FieldInfo {
        private Field field;
        private Class type;
        private String fieldName;
        private String snakeName;
        private String columnName;
        private boolean transients;

        public FieldInfo(Field field)  {
            this.field = field;
            doExtractField(this.field);
        }

        /**
         * 获得对应数据库字段名称
         * @param snake
         * @return
         */
        public String getName(boolean snake) {
            if(columnName != null) return columnName;
            if(snake) {
                return snakeName;
            }
            return fieldName;
        }

        /**
         * 解析该字段的需要的相关信息
         * @param field
         */
        private void doExtractField(Field field) {
            field.setAccessible(true);
            this.type = field.getType();
            this.fieldName= field.getName();
            this.snakeName = Utils.camelToSnake(this.fieldName);
            Column annotation = field.getAnnotation(Column.class);
            if(annotation != null) {
                this.columnName = annotation.name();
            }
            Transient transientAno = field.getAnnotation(Transient.class);
            this.transients = (transientAno != null);
        }

        public Field getField() {
            return field;
        }

        public Class getType() {
            return type;
        }

        public String getFieldName() {
            return fieldName;
        }

        public String getSnakeName() {
            return snakeName;
        }

        public String getColumnName() {
            return columnName;
        }

        public boolean isTransient() {
            return transients;
        }

        public Object getValue(Object obj) {
            try {
                return this.field.get(obj);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        public void setValue(T t, Object o) {
            try {
                field.set(t, o);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}