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

xin.xihc.jba.db.DB_MySql_Opera Maven / Gradle / Ivy

There is a newer version: 1.8.12
Show newest version
/**
 *
 */
package xin.xihc.jba.db;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xin.xihc.jba.annotation.Column;
import xin.xihc.jba.annotation.Index;
import xin.xihc.jba.core.JbaTemplate;
import xin.xihc.jba.db.bean.MysqlColumnInfo;
import xin.xihc.jba.db.bean.MysqlIndexInfo;
import xin.xihc.jba.scan.tables.InitDataInterface;
import xin.xihc.jba.scan.tables.properties.ColumnProperties;
import xin.xihc.jba.scan.tables.properties.IndexProperties;
import xin.xihc.jba.scan.tables.properties.TableProperties;
import xin.xihc.utils.common.CommonUtil;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * mysql数据库操作
 *
 * @author Leo.Xi
 * @version 1.3.0 不再支持java基本数据类型
 * @date 2018年1月24日
 * @code 增加:对java.sql.Date,Time,Timestamp字段的支持
 * @since 1.1.4
 */
public class DB_MySql_Opera implements I_TableOperation {

    /**
     * .@OnUpdateCurrentTimestamp注解适用的类型
     */
    public static final LinkedList> ON_UPDATE_APPLIED = new LinkedList<>();
    /** 日期、时间默认值 */
    public static final String DEFAULT_DATE_VALUE = "CURRENT_TIMESTAMP";
    /** char的最长的长度,超过后字段类型为varchar */
    private static final int CHAR_MAX_LENGTH = 64;
    /** varchar的最长的长度,超过字段类型为text */
    private static final int VARCHAR_MAX_LENGTH = 20000;
    private static final Logger LOGGER = LoggerFactory.getLogger(DB_MySql_Opera.class);
    /** java类型- mySQL类型 对应map */
    private static Map, String> JAVA_CLASS_TO_MYSQL_FIELDNAME = new HashMap<>();

    static {
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(byte.class, "tinyint");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(Byte.class, "tinyint");
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(short.class, "smallint");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(Short.class, "smallint");
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(int.class, "int");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(Integer.class, "int");
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(long.class, "bigint");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(Long.class, "bigint");
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(String.class, "char"); // String为默认,不需要加上
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(double.class, "double");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(Double.class, "double");
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(float.class, "double");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(Float.class, "double");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(BigDecimal.class, "decimal");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(java.util.Date.class, "datetime");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(java.sql.Timestamp.class, "timestamp");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(java.sql.Date.class, "date");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(java.sql.Time.class, "time");
        //		JAVA_CLASS_TO_MYSQL_FIELDNAME.put(boolean.class, "tinyint");
        JAVA_CLASS_TO_MYSQL_FIELDNAME.put(Boolean.class, "tinyint");

        ON_UPDATE_APPLIED.add(Date.class);
        ON_UPDATE_APPLIED.add(Timestamp.class);
    }

    /** 数据库schema */
    private String table_schema;

    /** ORM */
    private JbaTemplate jbaTemplate;

    public DB_MySql_Opera(JbaTemplate jbaTemplate) {
        this.jbaTemplate = jbaTemplate;
        table_schema = getSchema();
    }

    /**
     * 先获取当前数据库名
     *
     * @return
     */
    private String getSchema() {
        return jbaTemplate.queryColumn("select database()", null, String.class);
    }

    @Override
    public boolean isTableExists(final String tblName) {
        boolean res = false;
        String sql = "select COUNT(*) FROM information_schema.TABLES WHERE table_name ='" + tblName + "' AND table_schema='" + this.table_schema + "'";
        Integer count = jbaTemplate.queryColumn(sql, null, Integer.class);
        if (count > 0) {
            res = true;
        }
        return res;
    }

    @Override
    public void createTable(TableProperties tbl) {
        StringBuilder sql = new StringBuilder();
        sql.append("CREATE TABLE " + tbl.getTableName() + " ( ");
        for (ColumnProperties col : tbl.getColumns().values()) {
            sql.append(columnPro(col, null, true));
            sql.append(",");
        }
        // 更新索引
        String updateIndex = updateIndex(tbl, true);
        if (CommonUtil.isNotNullEmpty(updateIndex)) {
            sql.append(updateIndex);
            sql.append(",");
        }
        sql.deleteCharAt(sql.length() - 1)
                .append(") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT = '" + tbl.getRemark() + "';");
        jbaTemplate.executeSQL(sql.toString());

        // 初始化数据
        if (tbl.getTableBean() instanceof InitDataInterface) {
            CompletableFuture.runAsync(() -> ((InitDataInterface) tbl.getTableBean()).doInit(jbaTemplate))
                    .exceptionally(ex -> {
                        LOGGER.error("初始化表[" + tbl.getTableName() + "]数据异常:", ex);
                        return null;
                    });
        }
    }

    /**
     * 根据数据库列类型返回java数据类型
     *
     * @param dataType 列类型
     */
    private Class getClassByColumnDataType(String dataType) {
        Set> keySet = JAVA_CLASS_TO_MYSQL_FIELDNAME.keySet();
        for (Class key : keySet) {
            if (JAVA_CLASS_TO_MYSQL_FIELDNAME.get(key).equals(dataType)) {
                return key;
            }
        }
        return String.class;
    }

    /**
     * 将表中列的属性转为ColumnProperties
     *
     * @param dbColumns 表的列属性列表
     */
    private List convert2ColumnProperties(List dbColumns) {
        if (dbColumns.size() < 1) {
            return new ArrayList<>(0);
        }
        List result = new ArrayList<>(dbColumns.size());
        ColumnProperties prop;
        for (MysqlColumnInfo item : dbColumns) {
            prop = new ColumnProperties();
            prop.colName(item.getColumn_name());
            prop.type(getClassByColumnDataType(item.getData_type()));
            prop.defaultValue(CommonUtil.isNullEmpty(item.getColumn_default()) ? "" : item.getColumn_default());
            prop.remark(CommonUtil.isNullEmpty(item.getColumn_comment()) ? "" : item.getColumn_comment());
            prop.notNull("NO".equals(item.getIs_nullable()));
            if ("PRI".equals(item.getColumn_key())) { // 是主键
                prop.primary(true);
                if ("auto_increment".equalsIgnoreCase(item.getExtra())) { // 自增主键
                    prop.policy(Column.Policy.AUTO);
                    prop.length(item.getNumeric_precision());
                    prop.precision(item.getNumeric_scale());
                } else if (("varchar".equals(item.getData_type()) || "char".equals(item.getData_type())) && item
                        .getCharacter_maximum_length() == 32) {
                    prop.policy(Column.Policy.GUID);
                    prop.length(item.getCharacter_maximum_length());
                }
            }
            if ("varchar".equals(item.getData_type()) || "char".equals(item.getData_type())) {
                prop.charset(Column.TableCharset.toCharset(item.getCharacter_set_name()));
                prop.length(item.getCharacter_maximum_length());
            } else if ("text".equals(item.getData_type())) {// text字段长度为65535
                prop.length(65535);
            } else if (Number.class.isAssignableFrom(prop.type())) { // 如果是数值的
                prop.length(item.getNumeric_precision());
                prop.precision(item.getNumeric_scale());
            } else if (Date.class.isAssignableFrom(prop.type())) { // 如果是时间格式
                prop.precision(item.getDatetime_precision());
            }
            // 是否自动更新时间戳
            if ("on update CURRENT_TIMESTAMP".equalsIgnoreCase(item.getExtra())) {
                prop.onUpdateCurrentTimestamp(ON_UPDATE_APPLIED.contains(prop.type()));
            }
            result.add(prop);
        }
        return result;
    }

    @Override
    public void updateTable(TableProperties tbl) {
        List list = jbaTemplate.queryList(
                "select * from information_schema.columns where table_name = '" + tbl
                        .getTableName() + "' AND table_schema='" + this.table_schema + "'", null, MysqlColumnInfo.class,
                null);
        // 先获取表结构信息
        List dbColumnList = convert2ColumnProperties(list);

        ArrayList sqls = new ArrayList<>();
        // 已经存在的列
        ArrayList existsColumnsName = new ArrayList<>();
        StringBuilder sql = new StringBuilder();
        sql.append("ALTER TABLE ").append(tbl.getTableName()).append(" ");
        String after = "";
        for (ColumnProperties col : tbl.getColumns().values()) {
            Optional find = dbColumnList.stream().filter(t -> t.colName().equals(col.colName()))
                    .findFirst();
            //存在
            if (find.isPresent()) {
                ColumnProperties dbCol = find.get();
                if (!Objects.equals(JAVA_CLASS_TO_MYSQL_FIELDNAME.get(col.type()),
                        JAVA_CLASS_TO_MYSQL_FIELDNAME.get(dbCol.type())) || !col.equals(dbCol)) {
                    sqls.add("MODIFY " + columnPro(col, after, false));
                }
                existsColumnsName.add(col.colName());
            } else {
                sqls.add("ADD COLUMN " + columnPro(col, after, false));
            }
            after = col.colName();
        }
        for (ColumnProperties item : dbColumnList) {
            // 不包含的则是需要删除的
            if (existsColumnsName.contains(item.colName())) {
                continue;
            }
            sqls.add("DROP COLUMN " + item.colName());
        }
        // 更新索引
        String updateIndex = updateIndex(tbl, false);
        if (CommonUtil.isNotNullEmpty(updateIndex)) {
            sqls.add(updateIndex);
        }

        for (int i = 0; i < sqls.size(); i++) {
            sql.append(sqls.get(i)).append(",");
        }
        sql.append(" COMMENT = '").append(tbl.getRemark()).append("'");
        jbaTemplate.executeSQL(sql.toString());
    }

    /**
     * 需要设置精度
     *
     * @param clazz
     * @return
     */
    private boolean needPrecision(Class clazz) {
        if (clazz.equals(Double.class) || clazz.equals(double.class) || clazz.equals(float.class) || clazz
                .equals(Float.class)) {
            return true;
        }
        if (clazz.equals(BigDecimal.class)) {
            return true;
        }
        // 日期类需要设置精度
        if (Date.class.isAssignableFrom(clazz)) {
            return true;
        }
        return false;
    }

    /**
     * 不需要设置长度的
     *
     * @param clazz
     * @return
     */
    private boolean noNeedLength(Class clazz) {
        // 整形数字/Byte/Short/Integer/Long/Boolean
        if (clazz.equals(Byte.class) || clazz.equals(Short.class) || clazz.equals(Integer.class) || clazz
                .equals(Long.class) || clazz.equals(Boolean.class)) {
            return true;
        }
        if (clazz.equals(java.sql.Date.class)){
            return true;
        }
        return false;
    }

    /**
     * 得到字段名+属性拼接
     *
     * @param newCol    要创建的列的属性
     * @param after     列在after之后
     * @param isCreate  是否是创建表
     * @return
     */
    private String columnPro(ColumnProperties newCol, String after, boolean isCreate) {
        StringBuilder temp = new StringBuilder();
        temp.append(newCol.colName()).append(" ");

        boolean noNeedLength = noNeedLength(newCol.type()) || (newCol.type().equals(String.class) && newCol
                .length() > VARCHAR_MAX_LENGTH);

        boolean needPrecision = needPrecision(newCol.type());

        // 需要加列类型时
        if (noNeedLength) {
            if (newCol.type().equals(String.class)) {
                temp.append("text BINARY CHARACTER SET ").append(newCol.charset());
            } else {
                temp.append(JAVA_CLASS_TO_MYSQL_FIELDNAME.get(newCol.type()));
            }
        } else if (needPrecision) {
            temp.append(JAVA_CLASS_TO_MYSQL_FIELDNAME.get(newCol.type())).append("(");
            // 日期类需要设置精度
            if (!Date.class.isAssignableFrom(newCol.type())) {
                temp.append(newCol.length()).append(",");
            }
            temp.append(newCol.precision()).append(")");
        } else {
            if (newCol.length() > 0 && newCol.length() <= CHAR_MAX_LENGTH) {
                temp.append("char(").append(newCol.length()).append(") BINARY CHARACTER SET ").append(newCol.charset());
            } else {
                temp.append("varchar(").append(newCol.length()).append(") BINARY CHARACTER SET ").append(newCol.charset());
            }
        }


        // 允许为空
        if (CommonUtil.isNotNullEmpty(newCol.notNull()) && newCol.notNull()) {
            temp.append(" NOT NULL ");
        } else {
            temp.append(" NULL ");
        }

        // 主键
        if (newCol.primary() && newCol.policy().equals(Column.Policy.AUTO)) {
            temp.append(" AUTO_INCREMENT ");
        }

        // 默认值
        if (CommonUtil.isNotNullEmpty(newCol.defaultValue())) {
            if (Number.class.isAssignableFrom(newCol.type())) {
                temp.append(" DEFAULT ").append(newCol.defaultValue());
            } else if (newCol.type().equals(Date.class) || newCol.type().equals(Timestamp.class)) {
                if (newCol.defaultValue().contains(DEFAULT_DATE_VALUE)) {
                    temp.append(" DEFAULT ").append(newCol.defaultValue());
                }
            } else {
                temp.append(" DEFAULT '").append(newCol.defaultValue()).append("'");
            }
        } else if (!newCol.notNull()) {
            temp.append(" DEFAULT NULL ");
        }

        // 是否自动更新时间戳
        if (ON_UPDATE_APPLIED.contains(newCol.type()) && newCol.onUpdateCurrentTimestamp()) {
            temp.append(" ON UPDATE CURRENT_TIMESTAMP(").append(newCol.precision()).append(") ");
        }

        // 备注
        if (CommonUtil.isNotNullEmpty(newCol.remark())) {
            temp.append(" COMMENT '").append(newCol.remark()).append("'");
        }

        // 顺序
        if (!isCreate) {
            if (CommonUtil.isNotNullEmpty(after)) {
                temp.append(" AFTER ").append(after);
            } else {
                temp.append(" FIRST");
            }
        }
        return temp.toString();
    }

    @Override
    public void dropTable(TableProperties tbl) {
        jbaTemplate.executeSQL("DROP TABLE " + tbl.getTableName());
    }

    /**
     * 更新索引
     *
     * @param tbl     表
     * @param created 是否是创建
     * @return
     */
    public String updateIndex(TableProperties tbl, boolean created) {
        StringJoiner sql = new StringJoiner(",");
        // 需要创建的主键
        String newPrimary = tbl.getColumns().values().stream().filter(ColumnProperties::primary)
                .sorted(Comparator.comparing(ColumnProperties::policy)).map(ColumnProperties::colName)
                .collect(Collectors.joining(","));
        List dbIndexs = new LinkedList<>();
        if (!created) {
            dbIndexs = jbaTemplate
                    .queryList("SHOW index FROM " + tbl.getTableName(), null, MysqlIndexInfo.class, null);
        }
        String oldPrimary = dbIndexs.stream().filter(x -> "PRIMARY".equals(x.getKey_name()))
                .map(MysqlIndexInfo::getColumn_name).collect(Collectors.joining(","));

        // =---------------------------优先处理主键-----------------------=

        if (!newPrimary.equals(oldPrimary)) {
            if (CommonUtil.isNotNullEmpty(oldPrimary)) {
                sql.add("DROP PRIMARY KEY");
            }
            if (CommonUtil.isNotNullEmpty(newPrimary)) {
                String prefix = "";
                if (!created) {
                    prefix = "ADD";
                }
                sql.add(prefix + " PRIMARY KEY (" + newPrimary + ")");
            }
        }

        // 过滤掉主键索引
        Map> oldIndexs = dbIndexs.stream().filter(x -> !"PRIMARY".equals(x.getKey_name()))
                .collect(Collectors.groupingBy(
                        MysqlIndexInfo::getKey_name));
        // 已经存在的索引名称列表
        List alreadyExistsIndex = new LinkedList<>();
        List indexs = tbl.getIndexs();
        for (IndexProperties newIndex : indexs) {
            String indexName = newIndex.getIndexName();
            Index.IndexType type = newIndex.getType();
            String comment = newIndex.getRemark();
            String colNames = String.join(",", newIndex.getColumnNames());
            // 是否新增
            boolean add = false;
            // 存在则判断是否修改了
            if (oldIndexs.containsKey(indexName)) {
                alreadyExistsIndex.add(indexName);

                List mysqlIndexInfos = oldIndexs.get(indexName);
                Integer nonUnique = Math.max(mysqlIndexInfos.get(0).getNon_unique(), "FULLTEXT"
                        .equalsIgnoreCase(mysqlIndexInfos.get(0).getIndex_type()) ? 2 : 0);
                String oldColNames = mysqlIndexInfos.stream().map(MysqlIndexInfo::getColumn_name)
                        .collect(Collectors.joining(","));
                /// 索引类型或者列不一致
                if (!nonUnique.equals(type.ordinal()) || !oldColNames.equals(colNames)) {
                    sql.add("DROP INDEX " + indexName);
                    add = true;
                }
            } else {
                add = true;
            }
            // 添加
            if (add) {
                String prefix = "";
                if (!created) {
                    prefix = "ADD";
                }
                switch (type) {
                    case Unique:
                        sql.add(prefix + " UNIQUE INDEX " + indexName + " (" + colNames + ") COMMENT '" + comment + "'");
                        break;
                    case Normal:
                        sql.add(prefix + " INDEX " + indexName + " (" + colNames + ") COMMENT '" + comment + "'");
                        break;
                    case FullText:
                        sql.add(prefix + " FULLTEXT INDEX " + indexName + " (" + colNames + ") COMMENT '" + comment + "'");
                        break;
                    default:
                        break;
                }
            }
        }

        // 删除剩余的
        for (String indexName : oldIndexs.keySet()) {
            // 不存在列表里的删除
            if (!alreadyExistsIndex.contains(indexName)) {
                sql.add("DROP INDEX " + indexName);
            }
        }
        return sql.toString();
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy