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

com.javaoffers.batis.modelhelper.utils.TableHelper Maven / Gradle / Ivy

There is a newer version: 3.5.11.12
Show newest version
package com.javaoffers.batis.modelhelper.utils;

import com.javaoffers.batis.modelhelper.anno.BaseModel;
import com.javaoffers.batis.modelhelper.anno.BaseUnique;
import com.javaoffers.batis.modelhelper.anno.fun.parse.FunAnnoParser;
import com.javaoffers.batis.modelhelper.anno.fun.parse.ParseSqlFunResult;
import com.javaoffers.batis.modelhelper.constants.ModelHelpperConstants;
import com.javaoffers.batis.modelhelper.exception.BaseException;
import com.javaoffers.batis.modelhelper.exception.FindColException;
import com.javaoffers.batis.modelhelper.exception.ParseTableException;
import com.javaoffers.batis.modelhelper.filter.impl.AsSqlFunFilterImpl;
import com.javaoffers.batis.modelhelper.fun.ConstructorFun;
import com.javaoffers.batis.modelhelper.fun.GetterFun;
import com.javaoffers.batis.modelhelper.filter.TableHelperFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: Table Information Auxiliary Class
 * @Auther: create by cmj on 2022/5/2 02:05
 */
public class TableHelper {

    private final  static Map tableInfoMap = new ConcurrentHashMap<>();

    private final  static Map modelClass = new ConcurrentHashMap<>();

    private final static Map modelIsParse = new ConcurrentHashMap<>();

    private final static List interceptors = new ArrayList<>();

    static {
        interceptors.add(new AsSqlFunFilterImpl());
    }

    /**
     * Get all fields corresponding to Model. for select().colAll() parse
     *
     * @param modelClss
     * @return
     */
    public static List getColAllForSelect(Class modelClss) {
        List colAll = new LinkedList<>();
        TableInfo tableInfo = tableInfoMap.get(modelClss);
        String tableName = tableInfo.getTableName();
        tableInfo.getFieldNameColNameOfModel().forEach((fieldName, colName) -> {
            if (tableInfo.isSqlFun(colName)) {
                if(!tableInfo.colNameIsExcludeColAll(colName)){
                    //Because function fields in the query result resolution can't identify which belongs to the table name.
                    // So you can't to map the Class, so to be like this
                    colAll.add(colName + " as " + tableName + ModelHelpperConstants.SPLIT_LINE + fieldName);
                }
            } else if(!tableInfo.fieldNameIsExcludeColAll(fieldName)){
                colAll.add(tableName + "." + colName + " as " + tableName + ModelHelpperConstants.SPLIT_LINE + fieldName);
            }

        });
        return colAll;
    }

    public static List getColAllAndAliasNameOnly(Class modelClss) {
        List colAll = new LinkedList<>();
        TableInfo tableInfo = tableInfoMap.get(modelClss);
        tableInfo.getFieldNameColNameOfModel().forEach((colName, fieldName) -> {
            colAll.add(new SqlColInfo(tableInfo.getTableName(), colName, fieldName, tableInfo.isSqlFun(colName)));
        });
        return colAll;
    }

    public static Map> getColAllAndFieldOnly(Class modelClss) {
        TableInfo tableInfo = tableInfoMap.get(modelClss);
        Map> colNameOfModelField = tableInfo.getColNameAndFieldOfModel();
        return colNameOfModelField;
    }

    public static Map> getOriginalColAllAndFieldOnly(Class modelClss) {
        TableInfo tableInfo = tableInfoMap.get(modelClss);
        Map> colNameOfModelField = tableInfo.getOriginalColNameOfModelField();
        return colNameOfModelField;
    }

    /**
     * for select col
     * @param myFun
     * @return col info
     */
    public static String getColNameForSelect(GetterFun myFun) {
        SqlColInfo sqlColInfo = getSqlColInfo(myFun);
        String tableName = sqlColInfo.getTableName();
        String colName = sqlColInfo.getColNameNotBlank();
        String aliasName = sqlColInfo.getAliasName();
        if(sqlColInfo.isSqlFun()){
            colName = colName + " as " + tableName + ModelHelpperConstants.SPLIT_LINE + aliasName;
        }else{
            // append  ModelHelpperConstants.SPLIT_LINE.
            colName = tableName + "." + colName + " as " + tableName + ModelHelpperConstants.SPLIT_LINE + aliasName;
        }
        return colName;
    }

    public static Pair getSelectAggrColStatement(GetterFun myFun){
        SqlColInfo sqlColInfo = getSqlColInfo(myFun);
        String tableName = sqlColInfo.getTableName();
        String colName = sqlColInfo.getColNameNotBlank();
        String aliasName = tableName + ModelHelpperConstants.SPLIT_LINE + sqlColInfo.getAliasName();
        Pair pair = null;
        if(sqlColInfo.isSqlFun()){
            pair = Pair.of(colName, aliasName);
        }else{
            pair = Pair.of(tableName + "." + colName, aliasName);
        }
        return pair;
    }

    public static String getColNameNotAs(GetterFun myFun) {
        SqlColInfo sqlColInfo = getSqlColInfo(myFun);
        String colName = sqlColInfo.getColNameNotBlank();
        String tableName = sqlColInfo.getTableName();
        if(!sqlColInfo.isSqlFun()){
            colName = tableName + "." + colName;
        }
        return colName;
    }

    public static SqlColInfo getSqlColInfo(GetterFun myFun){
        String methodName = StringUtils.EMPTY;
        try {
            // 直接调用writeReplace
            Method writeReplace = myFun.getClass().getDeclaredMethod("writeReplace");
            writeReplace.setAccessible(true);
            Object sl = writeReplace.invoke(myFun);
            SerializedLambda serializedLambda = (SerializedLambda) sl;
            methodName = serializedLambda.getImplMethodName();
            String implClass = serializedLambda.getImplClass();
            TableInfo tableInfo = tableInfoMap.get(modelClass.get(implClass));
            Map colNameOfGetter = tableInfo.getMethodNameMappingFieldNameOfGetter();
            colNameOfGetter.computeIfAbsent(methodName, k -> {
                k = k.startsWith("get") ? k.substring(3) : k.startsWith("is") ? k.substring(2) : k;
                k = k.substring(0, 1).toLowerCase() + k.substring(1);
                return k;
            });

            String fieldName = colNameOfGetter.get(methodName);
            String colName = tableInfo.getFieldNameColNameOfModel().get(fieldName);
            return new SqlColInfo(tableInfo.getTableName(), colName, fieldName, tableInfo.isSqlFun(colName));
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new FindColException("Error parsing sql field : " + myFun.toString());
    }


    public static Pair getColNameAndAliasName(GetterFun myFun) {
        SqlColInfo selectColInfo = getSqlColInfo(myFun);
        String tableName = selectColInfo.getTableName();
        String colName = selectColInfo.getColName();
        String aliasName = selectColInfo.getAliasName();
        if(!selectColInfo.isSqlFun()){
            colName = tableName + "." + colName;
        }
        return Pair.of(colName, aliasName);
    }

    public static String getColNameOnly(GetterFun myFun) {
        SqlColInfo sqlColInfo = getSqlColInfo(myFun);
        return sqlColInfo.getColNameNotBlank();
    }

    /**
     * Parse table information.
     *
     * @param clazz
     */
    public static void parseTableInfo(Class clazz, Connection connection) {
        String name = clazz.getName().replaceAll("\\.", "/");
        modelClass.computeIfAbsent(name, k -> {
            try {
                //解析model class
                parseModelClass(clazz, connection);
                return clazz;
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }
        });
    }

    /**
     * Parse the model
     *
     * @param modelClazz Classes marked with the annotation @BaseModel
     */
    private static void parseModelClass(Class modelClazz, Connection connection) {
        Boolean isParse = modelIsParse.getOrDefault(modelClazz, false);
        if (!isParse) {
            synchronized (modelClazz) {
                isParse = modelIsParse.getOrDefault(modelClazz, false);
                if (!isParse) {
                    BaseModel table = modelClazz.getDeclaredAnnotation(BaseModel.class);
                    Assert.isTrue(table != null, "please use @BaseModel on class " + modelClazz.getName());
                    String tableName = table.value();
                    if (StringUtils.isBlank(tableName)) {
                        String simpleName = modelClazz.getSimpleName();
                        tableName = conLine(simpleName);
                    }

                    try {
                        TableInfo tableInfo = new TableInfo(tableName);
                        DatabaseMetaData metaData = connection.getMetaData();

                        ResultSet tableResultSet = metaData.getTables(connection.getCatalog(), connection.getSchema(), tableName, null);
                        ResultSet primaryKeys = metaData.getPrimaryKeys(connection.getCatalog(), connection.getSchema(), tableName);
                        LinkedList primaryKeyList = new LinkedList<>();
                        while (primaryKeys.next()) {
                            String primaryColName = primaryKeys.getString(ColumnLabel.COLUMN_NAME);
                            primaryKeyList.add(primaryColName);
                        }
                        while (tableResultSet.next()) {
                            // Get table field structure
                            ResultSet columnResultSet = metaData.getColumns(connection.getCatalog(), "", tableName, "%");
                            while (columnResultSet.next()) {
                                // Col Name
                                String columnName = columnResultSet.getString(ColumnLabel.COLUMN_NAME);
                                // type of data
                                String columnType = columnResultSet.getString(ColumnLabel.TYPE_NAME);
                                //the default value of the field
                                Object defaultValue = columnResultSet.getString(ColumnLabel.COLUMN_DEF);
                                //Whether to auto increment
                                boolean isAutoincrement = "YES".equalsIgnoreCase(columnResultSet.getString(ColumnLabel.IS_AUTOINCREMENT));
                                ColumnInfo columnInfo = new ColumnInfo(columnName, columnType, isAutoincrement, defaultValue);
                                tableInfo.getColumnInfos().add(columnInfo);
                                tableInfo.putColNames(columnName, columnInfo);
                                if (primaryKeyList.contains(columnName)) {
                                    tableInfo.putPrimaryColNames(columnName, columnInfo);
                                }
                            }
                        }
                        tableInfoMap.put(modelClazz, tableInfo);
                        Field[] colFs = Utils.getFields(modelClazz).toArray(new Field[]{});
                        boolean uniqueStatus = false;
                        for (Field colF : colFs) {
                            String colName = conLine(colF.getName());
                            BaseUnique baseUnique = colF.getDeclaredAnnotation(BaseUnique.class);
                            if (baseUnique != null) {
                                uniqueStatus = true;
                                if (StringUtils.isNotBlank(baseUnique.value())) {
                                    colName = baseUnique.value();
                                }
                            }
                            //parse @ColName and @funAnno
                            ParseSqlFunResult parseColName = FunAnnoParser.parse(tableInfo, modelClazz, colF, colName);
                            String fieldName = colF.getName();
                            boolean isFunSql = false;
                            boolean isExcludeColAll = false;
                            if (parseColName != null) {
                                isExcludeColAll = parseColName.isExcludeColAll();
                                colName = parseColName.getSqlFun();
                                // In select.colAll , insert.colAll, update.colAll will ignore this field.
                                if(isExcludeColAll){
                                    tableInfo.putFieldNameExcludeColAll(fieldName, isExcludeColAll);
                                }
                                isFunSql = parseColName.isFun();
                                tableInfo.putSqlFun(colName, isFunSql);
                                tableInfo.putColNameExcludeColAll(colName, isExcludeColAll);
                            } else {
                                // Indicates that this field does not have any annotation information.
                                // then the field must belong to a field in the original table
                                // Otherwise skip parsing of this field
                                if (!tableInfo.getColNames().containsKey(colName)) {
                                    continue;
                                }
                            }

                            //Avoid save or update operations that affect the database record,
                            // so we see it as SQL fun
                            if(!isFunSql){
                                for(AsSqlFunFilterImpl fieldFilter : interceptors){
                                    if(fieldFilter.filter(colF)){
                                        colName = tableName+"."+colName;
                                        isFunSql = true;
                                        tableInfo.putSqlFun(colName, true);
                                        break;
                                    }
                                }
                            }

                            // original table fields and sql-fun fields
                            tableInfo.putFieldNameColNameOfModel(fieldName, colName);
                            tableInfo.putColNameAndFieldOfModel(colName, colF);

                            //fieldName and field
                            tableInfo.putFieldNameAndField(fieldName, colF);

                            //not funSql and not excludeColAll and is originalColName
                            if (!isFunSql && !isExcludeColAll && tableInfo.getColNames().containsKey(colName)) {
                                //original table fields
                                tableInfo.putOriginalColNameAndFieldOfModelField(colName, colF);
                            }
                        }
                        Assert.isTrue(uniqueStatus, "Please declare @BaseUnique field in the model "+ modelClazz.getName());
                        tableInfo.unmodifiable();
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new ParseTableException(e.getMessage(), e);
                    }
                    modelIsParse.put(modelClazz, true);
                }
            }
        }
    }

    /**
     * turn underscore
     *
     * @param info
     * @return
     */
    private static String conLine(String info) {
        if (info.contains("_")) {
            return info;
        }

        String[] split = info.split("");
        StringBuilder builder = new StringBuilder();
        String last = null;
        for (String s : split) {
            if (StringUtils.isAllUpperCase(s)) {
                if (builder.length() != 0 && StringUtils.isAllLowerCase(last)) {
                    s = s.toLowerCase();
                    s = "_" + s;
                }
            }
            builder.append(s);
            last = s;
        }
        info = builder.toString();
        String defaultIgnoreCase = "YES";
        String ignoreCase = System.getProperty("IgnoreCase", defaultIgnoreCase);
        if (defaultIgnoreCase.equalsIgnoreCase(ignoreCase)) {
            info = info.toLowerCase();
        }
        return info;
    }

    /**
     * Parse the class corresponding to the constructor
     *
     * @param constructorFun
     * @param 
     * @return
     */
    public static  Class getClassFromConstructorFun(ConstructorFun constructorFun) {
        // 直接调用writeReplace
        String implClass = StringUtils.EMPTY;
        try {
            Method method = constructorFun.getClass().getDeclaredMethods()[0];
            method.setAccessible(true);
            Object sl = method.invoke(constructorFun);
            String lamdaName = sl.getClass().getName();
            implClass = lamdaName.replaceAll("\\.", "/");

        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(e.getMessage());
        }
        return modelClass.get(implClass);
    }

    /**
     * Parse the class corresponding to the constructor
     *
     * @param constructorFun
     * @param 
     * @return
     */
    public static  Class getClassFromConstructorFunForJoin(ConstructorFun constructorFun, DataSource dataSource) {

        // 直接调用writeReplace
        String implClass = StringUtils.EMPTY;
        Class clazz = null;
        Class aClass = null;
        Connection connection = null;
        try {
            try {
                Method method = constructorFun.getClass().getDeclaredMethods()[0];
                method.setAccessible(true);
                Object sl = method.invoke(constructorFun);
                clazz = sl.getClass();
                String lamdaName = clazz.getName();
                implClass = lamdaName.replaceAll("\\.", "/");

            } catch (Exception e) {
                e.printStackTrace();
            }
            aClass = modelClass.get(implClass);
            // if null, The description has not been parsed.
            // Maybe there is no corresponding Mapper for this class, we need to parse it again
            if (aClass == null) {
                try {
                    connection = dataSource.getConnection();
                    parseTableInfo(clazz, connection);
                    aClass = modelClass.get(implClass);
                    if (aClass == null) {
                        throw new ParseTableException("There was an error parsing the table, the table may not exist");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ParseTableException(e.getMessage());
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new ParseTableException(e.getMessage());
        } finally {
            try {
                if (connection != null && !connection.isClosed()) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
                throw new ParseTableException(e.getMessage());
            }
        }
        return aClass;
    }

    /**
     * get tableName by Class
     *
     * @param m2c
     * @param 
     * @return
     */
    public static  String getTableName(Class m2c) {
        return getTableInfo(m2c).getTableName();
    }

    /**
     * get TableInfo by class
     *
     * @param m2c
     * @return
     */
    public static TableInfo getTableInfo(Class m2c) {
        TableInfo tableInfo = tableInfoMap.get(m2c);
        return tableInfo;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy