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.springframework.dao.DataAccessException;
import org.springframework.transaction.annotation.Transactional;
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.tables.InitDataInterface;
import xin.xihc.jba.tables.properties.ColumnProperties;
import xin.xihc.jba.tables.properties.IndexProperties;
import xin.xihc.jba.tables.properties.TableProperties;
import xin.xihc.utils.common.CommonUtil;

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

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

	private static Map javaClassToMysqlFieldName = new HashMap<>();

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

	private String table_schema; // 数据库schema
	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(1) 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;
	}

	@Transactional(rollbackFor = DataAccessException.class)
	@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, null));
			sql.append(",");
		}
		String newPrimary = tbl.getColumns().values().stream().filter(x -> x.primary()).map(ColumnProperties::colName)
		                       .sorted().collect(Collectors.joining(","));
		sql.deleteCharAt(sql.length() - 1)
		   .append(") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT = '" + tbl.getRemark() + "';");
		jbaTemplate.executeSQL(sql.toString());

		// 初始化数据
		if (tbl.getTableBean() instanceof InitDataInterface) {
			Thread thread = new Thread(() -> ((InitDataInterface) tbl.getTableBean()).doInit(jbaTemplate));
			thread.setName("initData-" + tbl.getTableName());
			thread.start();
		}

		// 更新索引
		String updateIndex = updateIndex(tbl);
		if (CommonUtil.isNotNullEmpty(updateIndex)) {
			jbaTemplate.executeSQL("ALTER TABLE " + tbl.getTableName() + " " + updateIndex + ";");
		}
	}

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

	/**
	 * 将表中列的属性转为ColumnProperties
	 *
	 * @param dbColumns 表的列属性列表
	 * @return
	 */
	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".equals(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());
			}
			result.add(prop);
		}
		return result;
	}

	@Transactional(rollbackFor = DataAccessException.class)
	@Override
	public void updateTable(TableProperties tbl) {
		List list = jbaTemplate.queryMixModelList(
				"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 " + tbl.getTableName() + " ");
		String after = "";
		for (ColumnProperties col : tbl.getColumns().values()) {
			Optional find = dbColumnList.stream()
			                                              .filter(t -> t.colName().equalsIgnoreCase(col.colName()))
			                                              .findFirst();
			//存在
			if (find.isPresent()) {
				ColumnProperties dbCol = find.get();
				existsColumnsName.add(dbCol.colName());
				if (!Objects.equals(javaClassToMysqlFieldName.get(col.type()),
						javaClassToMysqlFieldName.get(dbCol.type())) || !dbCol.equals(col)) {// 如果对应的数据库字段类型不一样
					sqls.add("MODIFY " + columnPro(col, after, false, dbCol));
				}
				existsColumnsName.add(dbCol.colName());
			} else {
				sqls.add("ADD COLUMN " + columnPro(col, after, false, null));
			}
			after = col.colName();
		}
		for (ColumnProperties item : dbColumnList) {
			// 不包含的则是需要删除的
			if (existsColumnsName.contains(item.colName())) {
				continue;
			}
			sqls.add("DROP COLUMN " + item.colName());
		}
		// 更新索引
		String updateIndex = updateIndex(tbl);
		if (CommonUtil.isNotNullEmpty(updateIndex)) {
			sqls.add(updateIndex);
		}

		for (int i = 0; i < sqls.size(); i++) {
			sql.append(sqls.get(i) + ",");
		}
		sql.append(" COMMENT = '" + tbl.getRemark() + "'");
		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;
		}
		return false;
	}

	/**
	 * 得到字段名+属性拼接
	 *
	 * @param col      要创建的列的属性
	 * @param after    列在after之后
	 * @param isCreate 是否是创建表操作
	 * @param dbCol    数据库的列的属性,创建时没有
	 * @return
	 */
	private String columnPro(ColumnProperties col, String after, boolean isCreate, ColumnProperties dbCol) {
		StringBuilder temp = new StringBuilder();
		temp.append(col.colName() + " ");
		if (javaClassToMysqlFieldName.containsKey(col.type())) {
			temp.append(javaClassToMysqlFieldName.get(col.type()));
			if (needPrecision(col.type())) { // 需要精度的
				temp.append("(" + col.length() + "," + col.precision() + ")");
			}
		} else {
			if (CommonUtil.isNotNullEmpty(col.length()) && col.length() > 20000) {
				temp.append("text BINARY");
			} else {
				if (col.length() > 0 && col.length() <= 32) {
					temp.append("char(" + col.length() + ") BINARY");
				} else {
					temp.append("varchar(" + col.length() + ") BINARY");
				}
			}
			if (null == dbCol || !col.charset().equals(dbCol.charset())) {
				temp.append(" CHARACTER SET " + col.charset().name());
			}
		}
		if (CommonUtil.isNotNullEmpty(col.primary()) && col.primary()) {
			switch (col.policy()) {
				case AUTO:
					temp.append(" AUTO_INCREMENT ");
					break;
				default:
					break;
			}
		} else {
			if (CommonUtil.isNotNullEmpty(col.notNull()) && col.notNull()) {
				temp.append(" NOT NULL ");
			} else {
				temp.append(" NULL ");
			}
		}
		if (CommonUtil.isNotNullEmpty(col.remark())) {
			temp.append(" COMMENT '" + col.remark() + "'");
		}
		if (CommonUtil.isNotNullEmpty(col.defaultValue())) {
			if (col.type().equals(String.class)) {
				temp.append(" DEFAULT '" + col.defaultValue() + "'");
			} else if (col.type().isEnum()) {
				temp.append(" DEFAULT '" + col.defaultValue() + "'");
			} else if (Number.class.isAssignableFrom(col.type())) {
				temp.append(" DEFAULT " + col.defaultValue());
			} else if (col.type().equals(Timestamp.class)) {
				temp.append(" DEFAULT " + col.defaultValue());
			}
		} else if (!col.notNull()) {
			temp.append(" DEFAULT null ");
		}
		if (!isCreate) {
			if (CommonUtil.isNotNullEmpty(after)) {
				temp.append(" AFTER " + after);
			} else {
				temp.append(" FIRST");
			}
		}
		return temp.toString();
	}

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

	/**
	 * 更新索引
	 */
	public String updateIndex(TableProperties tbl) {
		StringJoiner sql = new StringJoiner(",");
		// 需要创建的主键
		String newPrimary = tbl.getColumns().values().stream().filter(x -> x.primary()).map(ColumnProperties::colName)
		                       .sorted().collect(Collectors.joining(","));

		List dbIndexs = jbaTemplate
				.queryMixModelList("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)) {
				sql.add("ADD PRIMARY KEY (" + newPrimary + ")");
			}
		}

		// 过滤掉主键索引
		Map> oldIndexs = dbIndexs.stream().filter(x -> !"PRIMARY".equals(x.getKey_name()))
		                                                      .collect(Collectors
				                                                      .groupingBy(MysqlIndexInfo::getKey_name));
		// 已经存在的索引名称列表
		List alreadyExistsIndex = new ArrayList<>();
		List indexs = tbl.getIndexs();
		Map> newIndexs = indexs.stream().collect(
				Collectors.groupingBy(IndexProperties::getIndexName));
		for (String indexName : newIndexs.keySet()) {
			List indexProperties = newIndexs.get(indexName);
			indexProperties.sort(Comparator.comparing(IndexProperties::getOrder));
			// 取第一个类型
			Index.IndexType type = indexProperties.get(0).getType();
			String comment = indexProperties.get(0).getRemark();
			String colNames = indexProperties.stream().map(IndexProperties::getColumnName)
			                                 .collect(Collectors.joining(","));
			// 是否新增
			boolean add = false;
			// 存在则判断是否修改了
			if (oldIndexs.containsKey(indexName)) {
				alreadyExistsIndex.add(indexName);

				List mysqlIndexInfos = oldIndexs.get(indexName);
				Integer non_unique = 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 (!non_unique.equals(type.ordinal()) || !oldColNames.equals(colNames)) {
					sql.add("DROP INDEX " + indexName);
					add = true;
				}
			} else {
				add = true;
			}
			// 添加
			if (add) {
				switch (type) {
					case Unique:
						sql.add("ADD UNIQUE INDEX " + indexName + " (" + colNames + ") COMMENT '" + comment + "'");
						break;
					case Normal:
						sql.add("ADD INDEX " + indexName + " (" + colNames + ") COMMENT '" + comment + "'");
						break;
					case FullText:
						sql.add("ADD 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