
com.jfirer.jsql.dbstructure.impl.MysqlSchemaAdjustment Maven / Gradle / Ivy
package com.jfirer.jsql.dbstructure.impl;
import com.jfirer.jsql.annotation.StandardColumnDef;
import com.jfirer.jsql.annotation.TableDef;
import com.jfirer.jsql.annotation.pkstrategy.AutoIncrement;
import com.jfirer.jsql.dbstructure.Constraint;
import com.jfirer.jsql.dbstructure.Index;
import com.jfirer.jsql.dbstructure.SchemaAdjustment;
import com.jfirer.jsql.metadata.TableEntityInfo;
import com.jfirer.jsql.metadata.TableMode;
import com.jfirer.baseutil.StringUtil;
import com.jfirer.baseutil.TRACEID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Date;
import java.sql.*;
import java.util.*;
public class MysqlSchemaAdjustment implements SchemaAdjustment
{
private final String findDatabase = "select database()";
private final String findTable = "SELECT count(*) from information_schema.TABLES where TABLE_SCHEMA=? and TABLE_NAME=?";
private final String findColumn = "SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,COLUMN_COMMENT from information_schema.`COLUMNS` where TABLE_SCHEMA=? and TABLE_NAME=? and COLUMN_NAME=?";
private final String addColumn = "ALTER TABLE {}.{} add {} ";
private final String findColumnNames = "SELECT COLUMN_NAME from information_schema.`COLUMNS` where TABLE_SCHEMA=? and TABLE_NAME=?";
private final String findIndexs = "SHOW INDEX FROM ";
private final String addIndex = "CREATE INDEX {} USING {} ON {}.{} ({}) ;";
private final String dropIndex = "ALTER TABLE {}.{} DROP INDEX `{}`";
private static final Logger logger = LoggerFactory.getLogger(MysqlSchemaAdjustment.class);
private void createTable(DataSource dataSource, Set tableEntityInfos) throws SQLException
{
Connection connection = dataSource.getConnection();
connection.setAutoCommit(false);
for (TableEntityInfo each : tableEntityInfos)
{
_createTable(each, connection);
}
connection.commit();
connection.setAutoCommit(true);
connection.close();
}
private void _createTable(TableEntityInfo tableEntityInfo, Connection connection) throws SQLException
{
Class> entityClass = tableEntityInfo.getEntityClass();
TableDef tableDef = entityClass.getAnnotation(TableDef.class);
String tableName = tableDef.name();
dropTableIfExist(connection, tableName);
createTable(connection, tableEntityInfo, tableDef, tableName);
}
private void createTable(Connection connection, TableEntityInfo info, TableDef tableDef, String tableName) throws SQLException
{
String createTableSql = generateCreateTableSql(info, tableDef, tableName);
logger.debug("traceId:{} 生成的建表语句是:{}", TRACEID.currentTraceId(), createTableSql);
PreparedStatement prepareStatement = connection.prepareStatement(createTableSql);
prepareStatement.executeUpdate();
prepareStatement.close();
}
private String generateCreateTableSql(TableEntityInfo each, TableDef tableDef, String tableName)
{
StringBuilder cache = new StringBuilder();
cache.append("CREATE TABLE `").append(tableName).append("` (");
createBody(each, cache);
for (TableEntityInfo.ColumnInfo info : each.getPropertyNameKeyMap().values())
{
String columnName = info.getColumnName();
if (info.getField().isAnnotationPresent(Constraint.class))
{
setConstraint(cache, info.getField(), columnName);
}
if (info.getField().isAnnotationPresent(Index.class))
{
setIndex(cache, info.getField(), columnName);
}
}
if (cache.lastIndexOf(",") != -1)
{
cache.deleteCharAt(cache.lastIndexOf(","));
}
cache.append(") ENGINE = InnoDB DEFAULT CHARSET = utf8 ");
if (StringUtil.isNotBlank(tableDef.comment()))
{
cache.append("comment=`").append(tableDef.comment()).append("`");
}
return cache.toString();
}
private void setIndex(StringBuilder cache, Field field, String columnName)
{
Index index = field.getAnnotation(Index.class);
String indexName = StringUtil.isNotBlank(index.indexName()) ? index.indexName() : columnName + "_idx_" + count.getAndIncrement();
if (index.unique())
{
cache.append("UNIQUE ");
}
cache.append("KEY `").append(indexName).append("` (`").append(columnName).append("`) USING ").append(index.indexType()).append(",\r\n");
}
private void setConstraint(StringBuilder cache, Field field, String columnName)
{
Constraint constraints = field.getAnnotation(Constraint.class);
switch (constraints.type())
{
case PRIMARY_KEY:
cache.append("PRIMARY KEY (`").append(columnName).append("`),\r\n");
break;
case UNIQUE_KEY:
String constraintName = StringUtil.isNotBlank(constraints.name()) ? constraints.name() : columnName + "_uni_" + count.getAndIncrement();
cache.append("UNIQUE KEY `").append(constraintName).append("` (`").append(columnName).append("`),\r\n");
break;
default:
break;
}
}
private void createBody(TableEntityInfo each, StringBuilder cache)
{
for (TableEntityInfo.ColumnInfo columnInfo : each.getPropertyNameKeyMap().values())
{
StandardColumnDef mysqlColumnDef = columnInfo.getField().getAnnotation(StandardColumnDef.class);
cache.append('`').append(columnInfo.getColumnName()).append("` ");
String columnType = decideColumnType(columnInfo.getField(), mysqlColumnDef);
cache.append(columnType).append(' ');
boolean isNullable = mysqlColumnDef == null || mysqlColumnDef.isNullable();
if (isNullable == false)
{
cache.append("NOT NULL ");
}
else
{
cache.append("NULL ");
}
if (columnInfo.getField().isAnnotationPresent(AutoIncrement.class))
{
cache.append("AUTO_INCREMENT ");
}
if (mysqlColumnDef != null && StringUtil.isNotBlank(mysqlColumnDef.comment()))
{
cache.append(" COMMENT '").append(mysqlColumnDef.comment()).append('\'');
}
cache.append(",\r\n");
}
}
private String decideColumnType(Field field, StandardColumnDef standardColumnDef)
{
String columnType;
if (standardColumnDef != null && StringUtil.isNotBlank(standardColumnDef.dataType()))
{
String dataType = standardColumnDef.dataType();
if ("varchar".equals(dataType))
{
columnType = "varchar(" + standardColumnDef.maxCharacterLength() + ")";
}
else if ("float".equals(dataType) || "double".equals(dataType))
{
columnType = dataType + "(" + standardColumnDef.numeric_precision() + "," + standardColumnDef.numeric_scale() + ")";
}
else if ("datetime".equals(dataType) || "timestamp".equals(dataType))
{
columnType = dataType + "(" + standardColumnDef.datetime_precision() + ")";
}
else
{
columnType = dataType;
}
}
else
{
Class> type = field.getType();
if (type == String.class)
{
columnType = "varchar(64)";
}
else if (type == Integer.class || type == int.class || type == short.class || type == Short.class || type == byte.class || type == Byte.class)
{
columnType = "int";
}
else if (type == boolean.class || type == Boolean.class)
{
columnType = "tinyint";
}
else if (type == long.class || type == Long.class)
{
columnType = "bigint";
}
else if (type == float.class || type == Float.class || type == Double.class || type == double.class)
{
columnType = "double";
}
else if (type == Date.class)
{
columnType = "timestamp";
}
else if (type == Time.class)
{
columnType = "time";
}
else if (type == java.util.Date.class || type == Timestamp.class || type == Calendar.class)
{
columnType = "timestamp(3)";
}
else if (type == Clob.class)
{
columnType = "text";
}
else if (type == Blob.class)
{
columnType = "blob";
}
else if (type == byte[].class)
{
columnType = "blob";
}
else
{
throw new UnsupportedOperationException("不支持的自动映射类型,请为属性" + field.getDeclaringClass().getName() + "." + field.getName() + "增加类定义注解");
}
}
return columnType;
}
private void dropTableIfExist(Connection connection, String tableName) throws SQLException
{
PreparedStatement prepareStatement = connection.prepareStatement("DROP TABLE IF EXISTS " + tableName);
prepareStatement.executeUpdate();
prepareStatement.close();
}
private void updateTable(DataSource dataSource, Set tableEntityInfos) throws SQLException
{
Connection connection = dataSource.getConnection();
connection.setAutoCommit(false);
String schema = getSchema(connection);
for (TableEntityInfo each : tableEntityInfos)
{
Class> entityClass = each.getEntityClass();
if (entityClass.isAnnotationPresent(TableDef.class) == false)
{
continue;
}
if (isTableExist(connection, schema, entityClass.getAnnotation(TableDef.class).name()) == false)
{
_createTable(each, connection);
continue;
}
String tableName = entityClass.getAnnotation(TableDef.class).name();
addMissingColumns(connection, schema, each, tableName);
dropNotExistColumns(connection, schema, tableName, each);
Map indexs = getIndexColumnNames(connection, tableName);
addMissingIndex(connection, schema, each, tableName, indexs);
dropNotExistIndexs(connection, schema, tableName, indexs);
}
connection.commit();
connection.setAutoCommit(true);
connection.close();
}
/**
* 添加表中还不存在的索引。如果该索引已经存在于表中,则从map中删除该数据
*
* @param connection
* @param schema
* @param each
* @param tableName
* @param indexs
* @throws SQLException
*/
private void addMissingIndex(Connection connection, String schema, TableEntityInfo each, String tableName, Map indexs) throws SQLException
{
for (TableEntityInfo.ColumnInfo info : each.getPropertyNameKeyMap().values())
{
if (info.getField().isAnnotationPresent(Index.class))
{
String columnName = info.getColumnName();
if (indexs.containsKey(columnName))
{
indexs.remove(columnName);
}
else
{
addIndex(connection, schema, tableName, info.getField(), columnName);
}
}
}
}
private void dropNotExistIndexs(Connection connection, String schema, String tableName, Map indexs) throws SQLException
{
if (indexs.isEmpty() == false)
{
for (String indexName : indexs.values())
{
String dropIndexSql = StringUtil.format(dropIndex, schema, tableName, indexName);
logger.debug("traceId:{} 准备删除索引:{}", TRACEID.currentTraceId(), dropIndexSql);
PreparedStatement prepareStatement = connection.prepareStatement(dropIndexSql);
prepareStatement.executeUpdate();
prepareStatement.close();
}
}
}
private void addIndex(Connection connection, String schema, String tableName, Field field, String columnName) throws SQLException
{
Index index = field.getAnnotation(Index.class);
String indexName = StringUtil.isNotBlank(index.indexName()) ? index.indexName() : columnName + "_idx_" + count.getAndIncrement();
String indexType = index.indexType();
String addIndexSql = StringUtil.format(addIndex, indexName, indexType, schema, tableName, columnName);
PreparedStatement prepareStatement = connection.prepareStatement(addIndexSql);
prepareStatement.executeUpdate();
prepareStatement.close();
}
/**
* 返回索引信息。key为列名,value为索引名称
*
* @param connection
* @param tableName
* @return
* @throws SQLException
*/
private Map getIndexColumnNames(Connection connection, String tableName) throws SQLException
{
String showIndexSql = findIndexs + tableName;
PreparedStatement prepareStatement = connection.prepareStatement(showIndexSql);
ResultSet resultSet = prepareStatement.executeQuery();
Map indexs = new HashMap();
while (resultSet.next())
{
indexs.put(resultSet.getString("Column_name"), resultSet.getString("Key_name"));
}
resultSet.close();
prepareStatement.close();
return indexs;
}
private void dropNotExistColumns(Connection connection, String schema, String tableName, TableEntityInfo info) throws SQLException
{
PreparedStatement prepareStatement = connection.prepareStatement(findColumnNames);
prepareStatement.setString(1, schema);
prepareStatement.setString(2, tableName);
ResultSet resultSet = prepareStatement.executeQuery();
List notExistColumnNames = new LinkedList();
while (resultSet.next())
{
String columnName = resultSet.getString(1);
if (info.getColumnInfoByColumnNameIgnoreCase(columnName) == null)
{
notExistColumnNames.add(columnName);
}
}
resultSet.close();
prepareStatement.close();
String dropColumnSqlPrefix = "ALTER TABLE " + tableName + " DROP COLUMN ";
for (String notExistColumnName : notExistColumnNames)
{
PreparedStatement prepareStatement2 = connection.prepareStatement(dropColumnSqlPrefix + notExistColumnName);
prepareStatement2.execute();
prepareStatement2.close();
}
}
private void addMissingColumns(Connection connection, String schema, TableEntityInfo each, String tableName) throws SQLException
{
for (TableEntityInfo.ColumnInfo columnInfo : each.getPropertyNameKeyMap().values())
{
StandardColumnDef columnDef = columnInfo.getField().getAnnotation(StandardColumnDef.class);
String columnName = columnInfo.getColumnName();
ColumnDefinition columnDefinition = queryColumnInfo(connection, schema, tableName, columnName);
if (columnDefinition == null)
{
addColumn(connection, schema, tableName, columnInfo.getField(), columnDef, columnName);
}
else
{
String columnType = decideColumnType(columnInfo.getField(), columnDef);
if (columnType.toLowerCase().startsWith(columnDefinition.dataType.toLowerCase()) == false)
{
String dropColumnSqlPrefix = "ALTER TABLE " + tableName + " DROP COLUMN ";
PreparedStatement ps = connection.prepareStatement(dropColumnSqlPrefix + columnDefinition.columnName);
ps.execute();
ps.close();
addColumn(connection, schema, tableName, columnInfo.getField(), columnDef, columnName);
}
}
}
}
private void addColumn(Connection connection, String schema, String tableName, Field field, StandardColumnDef columnDef, String columnName) throws SQLException
{
StringBuilder cache = new StringBuilder(StringUtil.format(addColumn, schema, tableName, columnName));
String columnType = decideColumnType(field, columnDef);
cache.append(columnType).append(' ');
boolean isNullable = columnDef != null && columnDef.isNullable();
if (isNullable == false)
{
cache.append("NOT NULL ");
}
if (field.isAnnotationPresent(AutoIncrement.class))
{
cache.append("AUTO_INCREMENT ");
}
if (columnDef != null && StringUtil.isNotBlank(columnDef.comment()))
{
cache.append(" COMMENT '").append(columnDef.comment()).append('\'');
}
String addColumnSql = cache.toString();
PreparedStatement prepareStatement = connection.prepareStatement(addColumnSql);
prepareStatement.executeUpdate();
prepareStatement.close();
}
class ColumnDefinition
{
String columnName;
String dataType;
boolean isNullable;
Integer maxCharactersLength;
String comment;
int numeric_precision;
int numeric_scale;
}
private ColumnDefinition queryColumnInfo(Connection connection, String schema, String tableName, String columnName) throws SQLException
{
PreparedStatement prepareStatement = connection.prepareStatement(findColumn);
prepareStatement.setString(1, schema);
prepareStatement.setString(2, tableName);
prepareStatement.setString(3, columnName);
ResultSet resultSet = prepareStatement.executeQuery();
if (resultSet.next() == false)
{
return null;
}
else
{
ColumnDefinition columnInfo = new ColumnDefinition();
columnInfo.columnName = resultSet.getString("COLUMN_NAME");
columnInfo.dataType = resultSet.getString("DATA_TYPE");
columnInfo.isNullable = resultSet.getString("IS_NULLABLE").equals("YES");
int CHARACTER_MAXIMUM_LENGTH = resultSet.getInt("CHARACTER_MAXIMUM_LENGTH");
columnInfo.maxCharactersLength = resultSet.wasNull() ? null : CHARACTER_MAXIMUM_LENGTH;
int NUMERIC_PRECISION = resultSet.getInt("NUMERIC_PRECISION");
columnInfo.numeric_precision = resultSet.wasNull() ? null : NUMERIC_PRECISION;
int NUMERIC_SCALE = resultSet.getInt("NUMERIC_SCALE");
columnInfo.numeric_scale = resultSet.wasNull() ? null : NUMERIC_SCALE;
columnInfo.comment = resultSet.getString("COLUMN_COMMENT");
return columnInfo;
}
}
private boolean isTableExist(Connection connection, String schema, String tableName) throws SQLException
{
PreparedStatement prepareStatement = connection.prepareStatement(findTable);
prepareStatement.setString(1, schema);
prepareStatement.setString(2, tableName);
ResultSet resultSet = prepareStatement.executeQuery();
resultSet.next();
int count = resultSet.getInt(1);
return count == 1;
}
private String getSchema(Connection connection) throws SQLException
{
PreparedStatement prepareStatement = connection.prepareStatement(findDatabase);
ResultSet resultSet = prepareStatement.executeQuery();
resultSet.next();
return resultSet.getString(1);
}
@Override
public void adjust(TableMode mode, DataSource dataSource, Set tableEntityInfos) throws SQLException
{
switch (mode)
{
case NONE:
break;
case CREATE:
createTable(dataSource, tableEntityInfos);
break;
case UPDATE:
updateTable(dataSource, tableEntityInfos);
break;
default:
break;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy