diff --git a/README.md b/README.md index ac2b2af57b99490b3fd4f2b5bc122729117d100e..891697c385a51763116746be4ea0a4072e32c56c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# mybatis-enhance-actable-1.0.7.RELEASE +# mybatis-enhance-actable-1.0.8.RELEASE 该项目是从之前写过的一个框架中抽取出来的,本身是对Mybatis做的增强功能,为了能够使习惯了hibernate框架的开发者能够快速的入手Mybatis,我给他取名叫做 “A.C.Table” 本意是自动建表的意思,A.C.Table是一个基于Spring和Mybatis的Maven项目,增强了Mybatis的功能,过配置model注解的方式来创建表,修改表结构,并且实现了共通的CUDR功能提升开发效率,目前仅支持Mysql,后续会扩展针对其他数据库的支持。 @@ -22,15 +22,18 @@ A.C.Table是采用了Spring、Mybatis技术的Maven结构,详细介绍如下 15. 修复query查询方法无法返回父类字段数据的bug(版本1.0.6.RELEASE) 16. 修复原本是主键,现在依然主键,修改该字段的其他信息时会报multiple primary key defined(例如id为int(11),改为int(10)后,就可重现次bug)(版本1.0.7.RELEASE) 17. 增加对字段备注的支持,使用方式@Column的comment属性(版本1.0.7.RELEASE) +18. 修复issues/IZ6WQ:bit类型的默认值设置失败,默认值可以使用0、1、true、false(版本1.0.8.RELEASE) +19. 修复issues/IYTJ1:使用@Unique进行联合约束,启动项目自动创建表结构后,删除联合约束报错(版本1.0.8.RELEASE) +20. 新增功能issues/IYW9F:mybatis.model.pack支持多包扫描","或者";"隔开(版本1.0.8.RELEASE) **基本使用规范** -1. 需要依赖mybatis-enhance-actable-1.0.7.RELEASE.jar +1. 需要依赖mybatis-enhance-actable-1.0.8.RELEASE.jar ``` com.gitee.sunchenbin.mybatis.actable mybatis-enhance-actable - 1.0.7.RELEASE + 1.0.8.RELEASE ``` @@ -79,7 +82,7 @@ A.C.Table是采用了Spring、Mybatis技术的Maven结构,详细介绍如下 com.gitee.sunchenbin.mybatis.actable mybatis-enhance-actable - 1.0.7.RELEASE + 1.0.8.RELEASE ``` @@ -106,7 +109,7 @@ A.C.Table是采用了Spring、Mybatis技术的Maven结构,详细介绍如下 com.gitee.sunchenbin.mybatis.actable mybatis-enhance-actable - 1.0.7.RELEASE + 1.0.8.RELEASE ``` diff --git a/mybatis-enhance-actable/README.md b/mybatis-enhance-actable/README.md index 0a8b22ec914b215e3b57f5d51f8ddebaaee3b696..f7b09e1b466d5b611f5774977bca2795a8000088 100644 --- a/mybatis-enhance-actable/README.md +++ b/mybatis-enhance-actable/README.md @@ -1,4 +1,4 @@ -# mybatis-enhance-actable-1.0.7.RELEASE +# mybatis-enhance-actable-1.0.8.RELEASE 该项目是从之前写过的一个框架中抽取出来的,本身是对Mybatis做的增强功能,为了能够使习惯了hibernate框架的开发者能够快速的入手Mybatis,我给他取名叫做 “A.C.Table” 本意是自动建表的意思,A.C.Table是一个基于Spring和Mybatis的Maven项目,增强了Mybatis的功能,过配置model注解的方式来创建表,修改表结构,并且实现了共通的CUDR功能提升开发效率,目前仅支持Mysql,后续会扩展针对其他数据库的支持。 @@ -22,15 +22,18 @@ A.C.Table是采用了Spring、Mybatis技术的Maven结构,详细介绍如下 15. 修复query查询方法无法返回父类字段数据的bug(版本1.0.6.RELEASE) 16. 修复原本是主键,现在依然主键,修改该字段的其他信息时会报multiple primary key defined(例如id为int(11),改为int(10)后,就可重现次bug)(版本1.0.7.RELEASE) 17. 增加对字段备注的支持,使用方式@Column的comment属性(版本1.0.7.RELEASE) +18. 修复issues/IZ6WQ:bit类型的默认值设置失败,默认值可以使用0、1、true、false(版本1.0.8.RELEASE) +19. 修复issues/IYTJ1:使用@Unique进行联合约束,启动项目自动创建表结构后,删除联合约束报错(版本1.0.8.RELEASE) +20. 新增功能issues/IYW9F:mybatis.model.pack支持多包扫描","或者";"隔开(版本1.0.8.RELEASE) **基本使用规范** -1. 需要依赖mybatis-enhance-actable-1.0.7.RELEASE.jar +1. 需要依赖mybatis-enhance-actable-1.0.8.RELEASE.jar ``` com.gitee.sunchenbin.mybatis.actable mybatis-enhance-actable - 1.0.7.RELEASE + 1.0.8.RELEASE ``` @@ -79,7 +82,7 @@ A.C.Table是采用了Spring、Mybatis技术的Maven结构,详细介绍如下 com.gitee.sunchenbin.mybatis.actable mybatis-enhance-actable - 1.0.7.RELEASE + 1.0.8.RELEASE ``` @@ -106,7 +109,7 @@ A.C.Table是采用了Spring、Mybatis技术的Maven结构,详细介绍如下 com.gitee.sunchenbin.mybatis.actable mybatis-enhance-actable - 1.0.7.RELEASE + 1.0.8.RELEASE ``` diff --git a/mybatis-enhance-actable/pom.xml b/mybatis-enhance-actable/pom.xml index a66d350d2e64fba8fdb1da4c49d2d87b0b1e0adb..e23382e6c46073b2fb849e8298b45e84d4d5e834 100644 --- a/mybatis-enhance-actable/pom.xml +++ b/mybatis-enhance-actable/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.gitee.sunchenbin.mybatis.actable mybatis-enhance-actable - 1.0.7.RELEASE + 1.0.8.RELEASE org.sonatype.oss diff --git a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/annotation/LengthCount.java b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/annotation/LengthCount.java index 2157ebdf9bbb956abfdd74d88dfa705516690e69..d7cf6247e05df410405a0b609c845d75c7589f6c 100644 --- a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/annotation/LengthCount.java +++ b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/annotation/LengthCount.java @@ -30,4 +30,5 @@ public @interface LengthCount{ * @return 默认是1,0表示不需要设置,1表示需要设置一个,2表示需要设置两个 */ public int LengthCount() default 1; + } diff --git a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/constants/MySqlTypeConstant.java b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/constants/MySqlTypeConstant.java index 00f58be527ccf62cecdadd630301aa5e779e7c05..6cb4dff23724e6135cd85dfa24e26eaaae95341d 100644 --- a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/constants/MySqlTypeConstant.java +++ b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/constants/MySqlTypeConstant.java @@ -45,7 +45,7 @@ public class MySqlTypeConstant { @LengthCount public static final String BIGINT = "bigint"; - @LengthCount(LengthCount=0) + @LengthCount(LengthCount=1) public static final String BIT = "bit"; @LengthCount(LengthCount=0) diff --git a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/manager/system/SysMysqlCreateTableManagerImpl.java b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/manager/system/SysMysqlCreateTableManagerImpl.java index 93d83cf7ecee2ac2e8200677a1d4f16de1dd7d2f..a0ddb355e5c8f0524dbd05bdc3a2a84363cab410 100644 --- a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/manager/system/SysMysqlCreateTableManagerImpl.java +++ b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/manager/system/SysMysqlCreateTableManagerImpl.java @@ -40,713 +40,779 @@ import com.gitee.sunchenbin.mybatis.actable.utils.ClassTools; @Service("sysMysqlCreateTableManager") public class SysMysqlCreateTableManagerImpl implements SysMysqlCreateTableManager { - private static final Logger log = LoggerFactory.getLogger(SysMysqlCreateTableManagerImpl.class); - - @Autowired - private CreateMysqlTablesMapper createMysqlTablesMapper; - - @Autowired - private ConfigurationUtil springContextUtil; - - // 获取Mysql的类型,以及类型需要设置几个长度 - private static Map mySqlTypeAndLengthMap = mySqlTypeAndLengthMap(); - - /** - * 要扫描的model所在的pack - */ - private static String pack = null; - - /** - * 自动创建模式:update表示更新,create表示删除原表重新创建 - */ - private static String tableAuto = null; - - /** - * 读取配置文件的三种状态(创建表、更新表、不做任何事情) - */ - public void createMysqlTable() { - // 读取配置信息 - pack = springContextUtil.getConfig(Constants.MODEL_PACK_KEY); - tableAuto = springContextUtil.getConfig(Constants.TABLE_AUTO_KEY); - - // 不做任何事情 - if (!"none".equals(tableAuto) && !"update".equals(tableAuto) && !"create".equals(tableAuto)) { - log.warn("配置mybatis.table.auto错误无法识别,当前配置只支持[none/update/create]三种类型!"); - return; - } - - // 不做任何事情 - if ("none".equals(tableAuto)) { - log.info("配置mybatis.table.auto=none,不需要做任何事情"); - return; - } - - // 从包package中获取所有的Class - Set> classes = ClassTools.getClasses(pack); - - // 初始化用于存储各种操作表结构的容器 - Map>> baseTableMap = initTableMap(); - - // 循环全部的model - for (Class clas : classes) { - - // 没有打注解不需要创建表 - if (null == clas.getAnnotation(Table.class)) { - continue; - } - // 构建出全部表的增删改的map - buildTableMapConstruct(clas, baseTableMap); - } - - // 根据传入的map,分别去创建或修改表结构 - createOrModifyTableConstruct(baseTableMap); - } - - /** - * 初始化用于存储各种操作表结构的容器 - * - * @return 初始化map - */ - private Map>> initTableMap() { - Map>> baseTableMap = new HashMap>>(); - // 1.用于存需要创建的表名+结构 - baseTableMap.put(Constants.NEW_TABLE_MAP, new HashMap>()); - // 2.用于存需要更新字段类型等的表名+结构 - baseTableMap.put(Constants.MODIFY_TABLE_MAP, new HashMap>()); - // 3.用于存需要增加字段的表名+结构 - baseTableMap.put(Constants.ADD_TABLE_MAP, new HashMap>()); - // 4.用于存需要删除字段的表名+结构 - baseTableMap.put(Constants.REMOVE_TABLE_MAP, new HashMap>()); - // 5.用于存需要删除主键的表名+结构 - baseTableMap.put(Constants.DROPKEY_TABLE_MAP, new HashMap>()); - // 6.用于存需要删除唯一约束的表名+结构 - baseTableMap.put(Constants.DROPINDEXANDUNIQUE_TABLE_MAP, new HashMap>()); - // 7.用于存需要增加的索引 - baseTableMap.put(Constants.ADDINDEX_TABLE_MAP, new HashMap>()); - // 8.用于存需要增加的唯一约束 - baseTableMap.put(Constants.ADDUNIQUE_TABLE_MAP, new HashMap>()); - return baseTableMap; - } - - /** - * 构建出全部表的增删改的map - * - * @param clas package中的model的Class - * @param baseTableMap 用于存储各种操作表结构的容器 - */ - private void buildTableMapConstruct(Class clas, Map>> baseTableMap) { - - // 获取model的table注解 - Table table = clas.getAnnotation(Table.class); - - // 1. 用于存表的全部字段 - List allFieldList = getAllFields(mySqlTypeAndLengthMap, clas); - if (allFieldList.size() == 0) { - log.warn("扫描model发现" + clas.getName() + "没有建表字段请检查!"); - return; - } - - // 如果配置文件配置的是create,表示将所有的表删掉重新创建 - if ("create".equals(tableAuto)) { - createMysqlTablesMapper.dorpTableByName(table.name()); - } - - // 先查该表是否以存在 - int exist = createMysqlTablesMapper.findTableCountByTableName(table.name()); - - // 不存在时 - if (exist == 0) { - baseTableMap.get(Constants.NEW_TABLE_MAP).put(table.name(), allFieldList); - baseTableMap.get(Constants.ADDINDEX_TABLE_MAP).put(table.name(), getAddIndexList(null, allFieldList)); - baseTableMap.get(Constants.ADDUNIQUE_TABLE_MAP).put(table.name(), getAddUniqueList(null, allFieldList)); - return; - } - - // 已存在时理论上做修改的操作,这里查出该表的结构 - List tableColumnList = createMysqlTablesMapper - .findTableEnsembleByTableName(table.name()); - - // 从sysColumns中取出我们需要比较的列的List - // 先取出name用来筛选出增加和删除的字段 - List columnNames = ClassTools.getPropertyValueList(tableColumnList, - SysMysqlColumns.COLUMN_NAME_KEY); - - // 验证对比从model中解析的allFieldList与从数据库查出来的columnList - // 2. 找出增加的字段 - List addFieldList = getAddFieldList(table, allFieldList, columnNames); - - // 3. 找出删除的字段 - List removeFieldList = getRemoveFieldList(table, columnNames, allFieldList); - - // 4. 找出更新的字段 - List modifyFieldList = getModifyFieldList(table, columnNames, tableColumnList, allFieldList); - - // 5. 找出需要删除主键的字段 - List dropKeyFieldList = getDropKeyFieldList(table, columnNames, tableColumnList, allFieldList); - - // 查询当前表中全部的索引和唯一约束 - Set allIndexAndUniqueNames = createMysqlTablesMapper.findTableIndexByTableName(table.name()); - - // 6. 找出需要删除的索引和唯一约束 - List dropIndexAndUniqueFieldList = getDropIndexAndUniqueList(allIndexAndUniqueNames, allFieldList); - - // 7. 找出需要新增的索引 - List addIndexFieldList = getAddIndexList(allIndexAndUniqueNames, allFieldList); - - // 8. 找出需要新增的唯一约束 - List addUniqueFieldList = getAddUniqueList(allIndexAndUniqueNames, allFieldList); - - if (addFieldList.size() != 0) { - baseTableMap.get(Constants.ADD_TABLE_MAP).put(table.name(), addFieldList); - } - if (removeFieldList.size() != 0) { - baseTableMap.get(Constants.REMOVE_TABLE_MAP).put(table.name(), removeFieldList); - } - if (modifyFieldList.size() != 0) { - baseTableMap.get(Constants.MODIFY_TABLE_MAP).put(table.name(), modifyFieldList); - } - if (dropKeyFieldList.size() != 0) { - baseTableMap.get(Constants.DROPKEY_TABLE_MAP).put(table.name(), dropKeyFieldList); - } - if (dropIndexAndUniqueFieldList.size() != 0) { - baseTableMap.get(Constants.DROPINDEXANDUNIQUE_TABLE_MAP).put(table.name(), dropIndexAndUniqueFieldList); - } - if (addIndexFieldList.size() != 0) { - baseTableMap.get(Constants.ADDINDEX_TABLE_MAP).put(table.name(), addIndexFieldList); - } - if (addUniqueFieldList.size() != 0) { - baseTableMap.get(Constants.ADDUNIQUE_TABLE_MAP).put(table.name(), addUniqueFieldList); - } - } - - /** - * 找出需要新建的索引 - * - * @param allIndexAndUniqueNames 当前数据库的索引很约束名 - * @param allFieldList model中的所有字段 - * @return 需要新建的索引 - */ - private List getAddIndexList(Set allIndexAndUniqueNames, List allFieldList) { - List addIndexFieldList = new ArrayList(); - if (null == allIndexAndUniqueNames) { - allIndexAndUniqueNames = new HashSet(); - } - for (Object obj : allFieldList) { - CreateTableParam createTableParam = (CreateTableParam) obj; - if (null != createTableParam.getFiledIndexName() && !allIndexAndUniqueNames.contains(createTableParam.getFiledIndexName())) { - addIndexFieldList.add(createTableParam); - } - } - return addIndexFieldList; - } - - /** - * 找出需要新建的唯一约束 - * - * @param allIndexAndUniqueNames 当前数据库的索引很约束名 - * @param allFieldList model中的所有字段 - * @return 需要新建的唯一约束 - */ - private List getAddUniqueList(Set allIndexAndUniqueNames, List allFieldList) { - List addUniqueFieldList = new ArrayList(); - if (null == allIndexAndUniqueNames) { - allIndexAndUniqueNames = new HashSet(); - } - for (Object obj : allFieldList) { - CreateTableParam createTableParam = (CreateTableParam) obj; - if (null != createTableParam.getFiledUniqueName() && !allIndexAndUniqueNames.contains(createTableParam.getFiledUniqueName())) { - addUniqueFieldList.add(createTableParam); - } - } - return addUniqueFieldList; - } - - /** - * 找出需要删除的索引和唯一约束 - * - * @param allIndexAndUniqueNames 当前数据库的索引很约束名 - * @param allFieldList model中的所有字段 - * @return 需要删除的索引和唯一约束 - */ - private List getDropIndexAndUniqueList(Set allIndexAndUniqueNames, List allFieldList) { - List dropIndexAndUniqueFieldList = new ArrayList(); - if (null == allIndexAndUniqueNames || allIndexAndUniqueNames.size() == 0) { - return dropIndexAndUniqueFieldList; - } - List currentModelIndexAndUnique = new ArrayList(); - for (Object obj : allFieldList) { - CreateTableParam createTableParam = (CreateTableParam) obj; - if (null != createTableParam.getFiledIndexName()) { - currentModelIndexAndUnique.add(createTableParam.getFiledIndexName()); - } - if (null != createTableParam.getFiledUniqueName()) { - currentModelIndexAndUnique.add(createTableParam.getFiledUniqueName()); - } - } - for (String string : allIndexAndUniqueNames) { - if (!currentModelIndexAndUnique.contains(string)) { - dropIndexAndUniqueFieldList.add(string); - } - } - return dropIndexAndUniqueFieldList; - } - - /** - * 返回需要删除主键的字段 - * - * @param table 表 - * @param columnNames 数据库中的结构 - * @param tableColumnList 表结构 - * @param allFieldList model中的所有字段 - * @return 需要删除主键的字段 - */ - private List getDropKeyFieldList(Table table, List columnNames, - List tableColumnList, List allFieldList) { - Map fieldMap = getAllFieldMap(allFieldList); - List dropKeyFieldList = new ArrayList(); - for (SysMysqlColumns sysColumn : tableColumnList) { - // 数据库中有该字段时 - CreateTableParam createTableParam = fieldMap.get(sysColumn.getColumn_name()); - if (createTableParam != null) { - // 原本是主键,现在不是了,那么要去做删除主键的操作 - if ("PRI".equals(sysColumn.getColumn_key()) && !createTableParam.isFieldIsKey()) { - dropKeyFieldList.add(createTableParam); - } - - } - } - return dropKeyFieldList; - } - - /** - * 根据数据库中表的结构和model中表的结构对比找出修改类型默认值等属性的字段 - * - * @param table 表 - * @param columnNames 数据库中的结构 - * @param tableColumnList 表结构 - * @param allFieldList model中的所有字段 - * @return 需要修改的字段 - */ - private List getModifyFieldList(Table table, List columnNames, List tableColumnList, - List allFieldList) { - Map fieldMap = getAllFieldMap(allFieldList); - List modifyFieldList = new ArrayList(); - for (SysMysqlColumns sysColumn : tableColumnList) { - // 数据库中有该字段时,验证是否有更新 - CreateTableParam createTableParam = fieldMap.get(sysColumn.getColumn_name()); - if (createTableParam != null) { - // 该复制操作时为了解决multiple primary key defined的同时又不会drop primary key - CreateTableParam modifyTableParam = createTableParam.clone(); - // 1.验证主键 - // 原本不是主键,现在变成了主键,那么要去做更新 - if (!"PRI".equals(sysColumn.getColumn_key()) && createTableParam.isFieldIsKey()) { - modifyFieldList.add(modifyTableParam); - continue; - } - // 原本是主键,现在依然主键,坚决不能在alter语句后加primary key,否则会报multiple primary key defined - if("PRI".equals(sysColumn.getColumn_key()) && createTableParam.isFieldIsKey()){ - modifyTableParam.setFieldIsKey(false); - } - // 2.验证类型 - if (!sysColumn.getData_type().toLowerCase().equals(createTableParam.getFieldType().toLowerCase())) { - modifyFieldList.add(modifyTableParam); - continue; - } - // 3.验证长度个小数点位数 - // 4.验证小数点位数 - int length = (Integer) mySqlTypeAndLengthMap.get(createTableParam.getFieldType().toLowerCase()); - String typeAndLength = createTableParam.getFieldType().toLowerCase(); - if (length == 1) { - // 拼接出类型加长度,比如varchar(1) - typeAndLength = typeAndLength + "(" + createTableParam.getFieldLength() + ")"; - } else if (length == 2) { - // 拼接出类型加长度,比如varchar(1) - typeAndLength = typeAndLength + "(" + createTableParam.getFieldLength() + "," - + createTableParam.getFieldDecimalLength() + ")"; - } - // 判断类型+长度是否相同 - if (!sysColumn.getColumn_type().toLowerCase().equals(typeAndLength)) { - modifyFieldList.add(modifyTableParam); - continue; - } - // 5.验证自增 - if ("auto_increment".equals(sysColumn.getExtra()) && !createTableParam.isFieldIsAutoIncrement()) { - modifyFieldList.add(modifyTableParam); - continue; - } - // 6.验证默认值 - if (sysColumn.getColumn_default() == null || sysColumn.getColumn_default().equals("")) { - // 数据库默认值是null,model中注解设置的默认值不为NULL时,那么需要更新该字段 - if (!"NULL".equals(createTableParam.getFieldDefaultValue())) { - modifyFieldList.add(modifyTableParam); - continue; - } - } else if (!sysColumn.getColumn_default().equals(createTableParam.getFieldDefaultValue())) { - // 两者不相等时,需要更新该字段 - modifyFieldList.add(modifyTableParam); - continue; - } - // 7.验证是否可以为null(主键不参与是否为null的更新) - if (sysColumn.getIs_nullable().equals("NO") && !createTableParam.isFieldIsKey()) { - if (createTableParam.isFieldIsNull()) { - // 一个是可以一个是不可用,所以需要更新该字段 - modifyFieldList.add(modifyTableParam); - continue; - } - } else if (sysColumn.getIs_nullable().equals("YES") && !createTableParam.isFieldIsKey()) { - if (!createTableParam.isFieldIsNull()) { - // 一个是可以一个是不可用,所以需要更新该字段 - modifyFieldList.add(modifyTableParam); - continue; - } - } - // 8.验证注释 - if (!sysColumn.getColumn_comment().equals(createTableParam.getFieldComment())) { - modifyFieldList.add(modifyTableParam); - } - } - } - return modifyFieldList; - } - - /** - * 将allFieldList转换为Map结构 - * - * @param allFieldList - * @return - */ - private Map getAllFieldMap(List allFieldList) { - // 将fieldList转成Map类型,字段名作为主键 - Map fieldMap = new HashMap(); - for (Object obj : allFieldList) { - CreateTableParam createTableParam = (CreateTableParam) obj; - fieldMap.put(createTableParam.getFieldName(), createTableParam); - } - return fieldMap; - } - - /** - * 根据数据库中表的结构和model中表的结构对比找出删除的字段 - * - * @param table 表 - * @param columnNames 数据库中的结构 - * @param allFieldList model中的所有字段 - */ - private List getRemoveFieldList(Table table, List columnNames, List allFieldList) { - Map fieldMap = getAllFieldMap(allFieldList); - // 用于存删除的字段 - List removeFieldList = new ArrayList(); - for (String fieldNm : columnNames) { - // 判断该字段在新的model结构中是否存在 - if (fieldMap.get(fieldNm) == null) { - // 不存在,做删除处理 - removeFieldList.add(fieldNm); - } - } - return removeFieldList; - } - - /** - * 根据数据库中表的结构和model中表的结构对比找出新增的字段 - * - * @param table 表 - * @param allFieldList model中的所有字段 - * @param columnNames 数据库中的结构 - * @return 新增的字段 - */ - private List getAddFieldList(Table table, List allFieldList, List columnNames) { - List addFieldList = new ArrayList(); - for (Object obj : allFieldList) { - CreateTableParam createTableParam = (CreateTableParam) obj; - // 循环新的model中的字段,判断是否在数据库中已经存在 - if (!columnNames.contains(createTableParam.getFieldName())) { - // 不存在,表示要在数据库中增加该字段 - addFieldList.add(obj); - } - } - return addFieldList; - } - - /** - * 迭代出所有model的所有fields - * - * @param mySqlTypeAndLengthMap mysql数据类型和对应几个长度的map - * @param clas 准备做为创建表依据的class - * @return 表的全部字段 - */ - private List getAllFields(Map mySqlTypeAndLengthMap, Class clas) { - List fieldList = new ArrayList(); - Field[] fields = clas.getDeclaredFields(); - - // 判断是否有父类,如果有拉取父类的field,这里只支持多层继承 - fields = recursionParents(clas, fields); - - for (Field field : fields) { - // 判断方法中是否有指定注解类型的注解 - boolean hasAnnotation = field.isAnnotationPresent(Column.class); - if (hasAnnotation) { - // 根据注解类型返回方法的指定类型注解 - Column column = field.getAnnotation(Column.class); - CreateTableParam param = new CreateTableParam(); - param.setFieldName(column.name()); - param.setFieldType(column.type().toLowerCase()); - param.setFieldLength(column.length()); - param.setFieldDecimalLength(column.decimalLength()); - // 主键时设置必须不为null - if (column.isKey()) { - param.setFieldIsNull(false); - } else { - param.setFieldIsNull(column.isNull()); - } - param.setFieldIsKey(column.isKey()); - param.setFieldIsAutoIncrement(column.isAutoIncrement()); - param.setFieldDefaultValue(column.defaultValue()); - param.setFieldComment(column.comment()); - int length = (Integer) mySqlTypeAndLengthMap.get(column.type().toLowerCase()); - param.setFileTypeLength(length); - // 获取当前字段的@Index注解 - Index index = field.getAnnotation(Index.class); - if (null != index) { - param.setFiledIndexName((index.name() == null || index.name().equals("")) ? (Constants.IDX + column.name()) : index.name()); - param.setFiledIndexValue(index.value().length == 0 ? Arrays.asList(column.name()) : Arrays.asList(index.value())); - } - // 获取当前字段的@Unique注解 - Unique unique = field.getAnnotation(Unique.class); - if (null != unique) { - param.setFiledUniqueName((unique.name() == null || unique.name().equals("")) ? (Constants.UNI + column.name()) : unique.name()); - param.setFiledUniqueValue(unique.value().length == 0 ? Arrays.asList(column.name()) : Arrays.asList(unique.value())); - } - fieldList.add(param); - } - } - return fieldList; - } - - /** - * 递归扫描父类的fields - * - * @param clas 类 - * @param fields 属性 - */ - @SuppressWarnings("rawtypes") - private Field[] recursionParents(Class clas, Field[] fields) { - if (clas.getSuperclass() != null) { - Class clsSup = clas.getSuperclass(); - List fieldList = new ArrayList(); - fieldList.addAll(Arrays.asList(fields)); - fieldList.addAll(Arrays.asList(clsSup.getDeclaredFields())); - fields = new Field[fieldList.size()]; - int i = 0; - for (Object field : fieldList.toArray()) { - fields[i] = (Field) field; - i++; - } - fields = recursionParents(clsSup, fields); - } - return fields; - } - - /** - * 根据传入的map创建或修改表结构 - * - * @param baseTableMap 操作sql的数据结构 - */ - private void createOrModifyTableConstruct(Map>> baseTableMap) { - // 1. 创建表 - createTableByMap(baseTableMap.get(Constants.NEW_TABLE_MAP)); - // 2. 删除要变更主键的表的原来的字段的主键 - dropFieldsKeyByMap(baseTableMap.get(Constants.DROPKEY_TABLE_MAP)); - // 3. 添加新的字段 - addFieldsByMap(baseTableMap.get(Constants.ADD_TABLE_MAP)); - // 4. 删除字段 - removeFieldsByMap(baseTableMap.get(Constants.REMOVE_TABLE_MAP)); - // 5. 修改字段类型等 - modifyFieldsByMap(baseTableMap.get(Constants.MODIFY_TABLE_MAP)); - // 6. 删除索引和约束 - dropIndexAndUniqueByMap(baseTableMap.get(Constants.DROPINDEXANDUNIQUE_TABLE_MAP)); - // 7. 创建索引 - addIndexByMap(baseTableMap.get(Constants.ADDINDEX_TABLE_MAP)); - // 8. 创建约束 - addUniqueByMap(baseTableMap.get(Constants.ADDUNIQUE_TABLE_MAP)); - - } - - /** - * 根据map结构删除索引和唯一约束 - * - * @param dropIndexAndUniqueMap 用于删除索引和唯一约束 - */ - private void dropIndexAndUniqueByMap(Map> dropIndexAndUniqueMap) { - if (dropIndexAndUniqueMap.size() > 0) { - for (Entry> entry : dropIndexAndUniqueMap.entrySet()) { - for (Object obj : entry.getValue()) { - Map map = new HashMap(); - map.put(entry.getKey(), obj); - log.info("开始删除表" + entry.getKey() + "中的索引" + obj); - createMysqlTablesMapper.dorpTabelIndex(map); - log.info("完成删除表" + entry.getKey() + "中的索引" + obj); - } - } - } - } - - /** - * 根据map结构创建索引 - * - * @param addIndexMap 用于创建索引和唯一约束 - */ - private void addIndexByMap(Map> addIndexMap) { - if (addIndexMap.size() > 0) { - for (Entry> entry : addIndexMap.entrySet()) { - for (Object obj : entry.getValue()) { - Map map = new HashMap(); - map.put(entry.getKey(), obj); - CreateTableParam fieldProperties = (CreateTableParam) obj; - if (null != fieldProperties.getFiledIndexName()) { - log.info("开始创建表" + entry.getKey() + "中的索引" + fieldProperties.getFiledIndexName()); - createMysqlTablesMapper.addTableIndex(map); - log.info("完成创建表" + entry.getKey() + "中的索引" + fieldProperties.getFiledIndexName()); - } - } - } - } - } - - /** - * 根据map结构创建唯一约束 - * - * @param addUniqueMap 用于创建索引和唯一约束 - */ - private void addUniqueByMap(Map> addUniqueMap) { - if (addUniqueMap.size() > 0) { - for (Entry> entry : addUniqueMap.entrySet()) { - for (Object obj : entry.getValue()) { - Map map = new HashMap(); - map.put(entry.getKey(), obj); - CreateTableParam fieldProperties = (CreateTableParam) obj; - if (null != fieldProperties.getFiledUniqueName()) { - log.info("开始创建表" + entry.getKey() + "中的唯一约束" + fieldProperties.getFiledUniqueName()); - createMysqlTablesMapper.addTableUnique(map); - log.info("完成创建表" + entry.getKey() + "中的唯一约束" + fieldProperties.getFiledUniqueName()); - } - } - } - } - } - - /** - * 根据map结构修改表中的字段类型等 - * - * @param modifyTableMap 用于存需要更新字段类型等的表名+结构 - */ - private void modifyFieldsByMap(Map> modifyTableMap) { - // 做修改字段操作 - if (modifyTableMap.size() > 0) { - for (Entry> entry : modifyTableMap.entrySet()) { - for (Object obj : entry.getValue()) { - Map map = new HashMap(); - map.put(entry.getKey(), obj); - CreateTableParam fieldProperties = (CreateTableParam) obj; - log.info("开始修改表" + entry.getKey() + "中的字段" + fieldProperties.getFieldName()); - createMysqlTablesMapper.modifyTableField(map); - log.info("完成修改表" + entry.getKey() + "中的字段" + fieldProperties.getFieldName()); - } - } - } - } - - /** - * 根据map结构删除表中的字段 - * - * @param removeTableMap 用于存需要删除字段的表名+结构 - */ - private void removeFieldsByMap(Map> removeTableMap) { - // 做删除字段操作 - if (removeTableMap.size() > 0) { - for (Entry> entry : removeTableMap.entrySet()) { - for (Object obj : entry.getValue()) { - Map map = new HashMap(); - map.put(entry.getKey(), obj); - String fieldName = (String) obj; - log.info("开始删除表" + entry.getKey() + "中的字段" + fieldName); - createMysqlTablesMapper.removeTableField(map); - log.info("完成删除表" + entry.getKey() + "中的字段" + fieldName); - } - } - } - } - - /** - * 根据map结构对表中添加新的字段 - * - * @param addTableMap 用于存需要增加字段的表名+结构 - */ - private void addFieldsByMap(Map> addTableMap) { - // 做增加字段操作 - if (addTableMap.size() > 0) { - for (Entry> entry : addTableMap.entrySet()) { - for (Object obj : entry.getValue()) { - Map map = new HashMap(); - map.put(entry.getKey(), obj); - CreateTableParam fieldProperties = (CreateTableParam) obj; - log.info("开始为表" + entry.getKey() + "增加字段" + fieldProperties.getFieldName()); - createMysqlTablesMapper.addTableField(map); - log.info("完成为表" + entry.getKey() + "增加字段" + fieldProperties.getFieldName()); - } - } - } - } - - /** - * 根据map结构删除要变更表中字段的主键 - * - * @param dropKeyTableMap 用于存需要删除主键的表名+结构 - */ - private void dropFieldsKeyByMap(Map> dropKeyTableMap) { - // 先去做删除主键的操作,这步操作必须在增加和修改字段之前! - if (dropKeyTableMap.size() > 0) { - for (Entry> entry : dropKeyTableMap.entrySet()) { - for (Object obj : entry.getValue()) { - Map map = new HashMap(); - map.put(entry.getKey(), obj); - CreateTableParam fieldProperties = (CreateTableParam) obj; - log.info("开始为表" + entry.getKey() + "删除主键" + fieldProperties.getFieldName()); - createMysqlTablesMapper.dropKeyTableField(map); - log.info("完成为表" + entry.getKey() + "删除主键" + fieldProperties.getFieldName()); - } - } - } - } - - /** - * 根据map结构创建表 - * - * @param newTableMap 用于存需要创建的表名+结构 - */ - private void createTableByMap(Map> newTableMap) { - // 做创建表操作 - if (newTableMap.size() > 0) { - for (Entry> entry : newTableMap.entrySet()) { - Map> map = new HashMap>(); - map.put(entry.getKey(), entry.getValue()); - log.info("开始创建表:" + entry.getKey()); - createMysqlTablesMapper.createTable(map); - log.info("完成创建表:" + entry.getKey()); - } - } - } - - /** - * 获取Mysql的类型,以及类型需要设置几个长度,这里构建成map的样式 - * 构建Map(字段名(小写),需要设置几个长度(0表示不需要设置,1表示需要设置一个,2表示需要设置两个)) - */ - public static Map mySqlTypeAndLengthMap() { - Field[] fields = MySqlTypeConstant.class.getDeclaredFields(); - Map map = new HashMap(); - for (Field field : fields) { - LengthCount lengthCount = field.getAnnotation(LengthCount.class); - map.put(field.getName().toLowerCase(), lengthCount.LengthCount()); - } - return map; - } + private static final Logger log = LoggerFactory.getLogger(SysMysqlCreateTableManagerImpl.class); + + @Autowired + private CreateMysqlTablesMapper createMysqlTablesMapper; + + @Autowired + private ConfigurationUtil springContextUtil; + + // 获取Mysql的类型,以及类型需要设置几个长度 + private static Map mySqlTypeAndLengthMap = mySqlTypeAndLengthMap(); + + /** + * 要扫描的model所在的pack + */ + private static String pack = null; + + /** + * 自动创建模式:update表示更新,create表示删除原表重新创建 + */ + private static String tableAuto = null; + + /** + * 读取配置文件的三种状态(创建表、更新表、不做任何事情) + */ + public void createMysqlTable() { + // 读取配置信息 + pack = springContextUtil.getConfig(Constants.MODEL_PACK_KEY); + tableAuto = springContextUtil.getConfig(Constants.TABLE_AUTO_KEY); + + // 不做任何事情 + if (!"none".equals(tableAuto) && !"update".equals(tableAuto) && !"create".equals(tableAuto)) { + log.warn("配置mybatis.table.auto错误无法识别,当前配置只支持[none/update/create]三种类型!"); + return; + } + + // 不做任何事情 + if ("none".equals(tableAuto)) { + log.info("配置mybatis.table.auto=none,不需要做任何事情"); + return; + } + + // 从包package中获取所有的Class + Set> classes = ClassTools.getClasses(pack); + + // 初始化用于存储各种操作表结构的容器 + Map>> baseTableMap = initTableMap(); + + // 循环全部的model + for (Class clas : classes) { + + // 没有打注解不需要创建表 + if (null == clas.getAnnotation(Table.class)) { + continue; + } + // 构建出全部表的增删改的map + buildTableMapConstruct(clas, baseTableMap); + } + + // 根据传入的map,分别去创建或修改表结构 + createOrModifyTableConstruct(baseTableMap); + } + + /** + * 初始化用于存储各种操作表结构的容器 + * + * @return 初始化map + */ + private Map>> initTableMap() { + Map>> baseTableMap = new HashMap>>(); + // 1.用于存需要创建的表名+结构 + baseTableMap.put(Constants.NEW_TABLE_MAP, new HashMap>()); + // 2.用于存需要更新字段类型等的表名+结构 + baseTableMap.put(Constants.MODIFY_TABLE_MAP, new HashMap>()); + // 3.用于存需要增加字段的表名+结构 + baseTableMap.put(Constants.ADD_TABLE_MAP, new HashMap>()); + // 4.用于存需要删除字段的表名+结构 + baseTableMap.put(Constants.REMOVE_TABLE_MAP, new HashMap>()); + // 5.用于存需要删除主键的表名+结构 + baseTableMap.put(Constants.DROPKEY_TABLE_MAP, new HashMap>()); + // 6.用于存需要删除唯一约束的表名+结构 + baseTableMap.put(Constants.DROPINDEXANDUNIQUE_TABLE_MAP, new HashMap>()); + // 7.用于存需要增加的索引 + baseTableMap.put(Constants.ADDINDEX_TABLE_MAP, new HashMap>()); + // 8.用于存需要增加的唯一约束 + baseTableMap.put(Constants.ADDUNIQUE_TABLE_MAP, new HashMap>()); + return baseTableMap; + } + + /** + * 构建出全部表的增删改的map + * + * @param clas + * package中的model的Class + * @param baseTableMap + * 用于存储各种操作表结构的容器 + */ + private void buildTableMapConstruct(Class clas, Map>> baseTableMap) { + + // 获取model的table注解 + Table table = clas.getAnnotation(Table.class); + + // 1. 用于存表的全部字段 + List allFieldList = getAllFields(mySqlTypeAndLengthMap, clas); + if (allFieldList.size() == 0) { + log.warn("扫描model发现" + clas.getName() + "没有建表字段请检查!"); + return; + } + + // 如果配置文件配置的是create,表示将所有的表删掉重新创建 + if ("create".equals(tableAuto)) { + createMysqlTablesMapper.dorpTableByName(table.name()); + } + + // 先查该表是否以存在 + int exist = createMysqlTablesMapper.findTableCountByTableName(table.name()); + + // 不存在时 + if (exist == 0) { + baseTableMap.get(Constants.NEW_TABLE_MAP).put(table.name(), allFieldList); + baseTableMap.get(Constants.ADDINDEX_TABLE_MAP).put(table.name(), getAddIndexList(null, allFieldList)); + baseTableMap.get(Constants.ADDUNIQUE_TABLE_MAP).put(table.name(), getAddUniqueList(null, allFieldList)); + return; + } + + // 已存在时理论上做修改的操作,这里查出该表的结构 + List tableColumnList = createMysqlTablesMapper.findTableEnsembleByTableName(table.name()); + + // 从sysColumns中取出我们需要比较的列的List + // 先取出name用来筛选出增加和删除的字段 + List columnNames = ClassTools.getPropertyValueList(tableColumnList, SysMysqlColumns.COLUMN_NAME_KEY); + + // 验证对比从model中解析的allFieldList与从数据库查出来的columnList + // 2. 找出增加的字段 + List addFieldList = getAddFieldList(table, allFieldList, columnNames); + + // 3. 找出删除的字段 + List removeFieldList = getRemoveFieldList(table, columnNames, allFieldList); + + // 4. 找出更新的字段 + List modifyFieldList = getModifyFieldList(table, columnNames, tableColumnList, allFieldList); + + // 5. 找出需要删除主键的字段 + List dropKeyFieldList = getDropKeyFieldList(table, columnNames, tableColumnList, allFieldList); + + // 查询当前表中全部的索引和唯一约束 + Set allIndexAndUniqueNames = createMysqlTablesMapper.findTableIndexByTableName(table.name()); + + // 6. 找出需要删除的索引和唯一约束 + List dropIndexAndUniqueFieldList = getDropIndexAndUniqueList(allIndexAndUniqueNames, allFieldList); + + // 7. 找出需要新增的索引 + List addIndexFieldList = getAddIndexList(allIndexAndUniqueNames, allFieldList); + + // 8. 找出需要新增的唯一约束 + List addUniqueFieldList = getAddUniqueList(allIndexAndUniqueNames, allFieldList); + + if (addFieldList.size() != 0) { + baseTableMap.get(Constants.ADD_TABLE_MAP).put(table.name(), addFieldList); + } + if (removeFieldList.size() != 0) { + baseTableMap.get(Constants.REMOVE_TABLE_MAP).put(table.name(), removeFieldList); + } + if (modifyFieldList.size() != 0) { + baseTableMap.get(Constants.MODIFY_TABLE_MAP).put(table.name(), modifyFieldList); + } + if (dropKeyFieldList.size() != 0) { + baseTableMap.get(Constants.DROPKEY_TABLE_MAP).put(table.name(), dropKeyFieldList); + } + if (dropIndexAndUniqueFieldList.size() != 0) { + baseTableMap.get(Constants.DROPINDEXANDUNIQUE_TABLE_MAP).put(table.name(), dropIndexAndUniqueFieldList); + } + if (addIndexFieldList.size() != 0) { + baseTableMap.get(Constants.ADDINDEX_TABLE_MAP).put(table.name(), addIndexFieldList); + } + if (addUniqueFieldList.size() != 0) { + baseTableMap.get(Constants.ADDUNIQUE_TABLE_MAP).put(table.name(), addUniqueFieldList); + } + } + + /** + * 找出需要新建的索引 + * + * @param allIndexAndUniqueNames + * 当前数据库的索引很约束名 + * @param allFieldList + * model中的所有字段 + * @return 需要新建的索引 + */ + private List getAddIndexList(Set allIndexAndUniqueNames, List allFieldList) { + List addIndexFieldList = new ArrayList(); + if (null == allIndexAndUniqueNames) { + allIndexAndUniqueNames = new HashSet(); + } + for (Object obj : allFieldList) { + CreateTableParam createTableParam = (CreateTableParam) obj; + if (null != createTableParam.getFiledIndexName() + && !allIndexAndUniqueNames.contains(createTableParam.getFiledIndexName())) { + addIndexFieldList.add(createTableParam); + } + } + return addIndexFieldList; + } + + /** + * 找出需要新建的唯一约束 + * + * @param allIndexAndUniqueNames + * 当前数据库的索引很约束名 + * @param allFieldList + * model中的所有字段 + * @return 需要新建的唯一约束 + */ + private List getAddUniqueList(Set allIndexAndUniqueNames, List allFieldList) { + List addUniqueFieldList = new ArrayList(); + if (null == allIndexAndUniqueNames) { + allIndexAndUniqueNames = new HashSet(); + } + for (Object obj : allFieldList) { + CreateTableParam createTableParam = (CreateTableParam) obj; + if (null != createTableParam.getFiledUniqueName() + && !allIndexAndUniqueNames.contains(createTableParam.getFiledUniqueName())) { + addUniqueFieldList.add(createTableParam); + } + } + return addUniqueFieldList; + } + + /** + * 找出需要删除的索引和唯一约束 + * + * @param allIndexAndUniqueNames + * 当前数据库的索引很约束名 + * @param allFieldList + * model中的所有字段 + * @return 需要删除的索引和唯一约束 + */ + private List getDropIndexAndUniqueList(Set allIndexAndUniqueNames, List allFieldList) { + List dropIndexAndUniqueFieldList = new ArrayList(); + if (null == allIndexAndUniqueNames || allIndexAndUniqueNames.size() == 0) { + return dropIndexAndUniqueFieldList; + } + List currentModelIndexAndUnique = new ArrayList(); + for (Object obj : allFieldList) { + CreateTableParam createTableParam = (CreateTableParam) obj; + if (null != createTableParam.getFiledIndexName()) { + currentModelIndexAndUnique.add(createTableParam.getFiledIndexName()); + } + if (null != createTableParam.getFiledUniqueName()) { + currentModelIndexAndUnique.add(createTableParam.getFiledUniqueName()); + } + } + for (String string : allIndexAndUniqueNames) { + if (!currentModelIndexAndUnique.contains(string)) { + dropIndexAndUniqueFieldList.add(string); + } + } + return dropIndexAndUniqueFieldList; + } + + /** + * 返回需要删除主键的字段 + * + * @param table + * 表 + * @param columnNames + * 数据库中的结构 + * @param tableColumnList + * 表结构 + * @param allFieldList + * model中的所有字段 + * @return 需要删除主键的字段 + */ + private List getDropKeyFieldList(Table table, List columnNames, + List tableColumnList, List allFieldList) { + Map fieldMap = getAllFieldMap(allFieldList); + List dropKeyFieldList = new ArrayList(); + for (SysMysqlColumns sysColumn : tableColumnList) { + // 数据库中有该字段时 + CreateTableParam createTableParam = fieldMap.get(sysColumn.getColumn_name()); + if (createTableParam != null) { + // 原本是主键,现在不是了,那么要去做删除主键的操作 + if ("PRI".equals(sysColumn.getColumn_key()) && !createTableParam.isFieldIsKey()) { + dropKeyFieldList.add(createTableParam); + } + + } + } + return dropKeyFieldList; + } + + /** + * 根据数据库中表的结构和model中表的结构对比找出修改类型默认值等属性的字段 + * + * @param table + * 表 + * @param columnNames + * 数据库中的结构 + * @param tableColumnList + * 表结构 + * @param allFieldList + * model中的所有字段 + * @return 需要修改的字段 + */ + private List getModifyFieldList(Table table, List columnNames, + List tableColumnList, List allFieldList) { + Map fieldMap = getAllFieldMap(allFieldList); + List modifyFieldList = new ArrayList(); + for (SysMysqlColumns sysColumn : tableColumnList) { + // 数据库中有该字段时,验证是否有更新 + CreateTableParam createTableParam = fieldMap.get(sysColumn.getColumn_name()); + if (createTableParam != null) { + // 该复制操作时为了解决multiple primary key defined的同时又不会drop primary key + CreateTableParam modifyTableParam = createTableParam.clone(); + // 1.验证主键 + // 原本不是主键,现在变成了主键,那么要去做更新 + if (!"PRI".equals(sysColumn.getColumn_key()) && createTableParam.isFieldIsKey()) { + modifyFieldList.add(modifyTableParam); + continue; + } + // 原本是主键,现在依然主键,坚决不能在alter语句后加primary key,否则会报multiple primary + // key defined + if ("PRI".equals(sysColumn.getColumn_key()) && createTableParam.isFieldIsKey()) { + modifyTableParam.setFieldIsKey(false); + } + // 2.验证类型 + if (!sysColumn.getData_type().toLowerCase().equals(createTableParam.getFieldType().toLowerCase())) { + modifyFieldList.add(modifyTableParam); + continue; + } + // 3.验证长度个小数点位数 + // 4.验证小数点位数 + int length = (Integer) mySqlTypeAndLengthMap.get(createTableParam.getFieldType().toLowerCase()); + String typeAndLength = createTableParam.getFieldType().toLowerCase(); + if (length == 1) { + // 拼接出类型加长度,比如varchar(1) + typeAndLength = typeAndLength + "(" + createTableParam.getFieldLength() + ")"; + } else if (length == 2) { + // 拼接出类型加长度,比如varchar(1) + typeAndLength = typeAndLength + "(" + createTableParam.getFieldLength() + "," + + createTableParam.getFieldDecimalLength() + ")"; + } + + // 判断类型+长度是否相同 + if (!sysColumn.getColumn_type().toLowerCase().equals(typeAndLength)) { + modifyFieldList.add(modifyTableParam); + continue; + } + // 5.验证自增 + if ("auto_increment".equals(sysColumn.getExtra()) && !createTableParam.isFieldIsAutoIncrement()) { + modifyFieldList.add(modifyTableParam); + continue; + } + // 6.验证默认值 + if (sysColumn.getColumn_default() == null || sysColumn.getColumn_default().equals("")) { + // 数据库默认值是null,model中注解设置的默认值不为NULL时,那么需要更新该字段 + if (!"NULL".equals(createTableParam.getFieldDefaultValue())) { + modifyFieldList.add(modifyTableParam); + continue; + } + } else if (!sysColumn.getColumn_default().equals(createTableParam.getFieldDefaultValue())) { + // 两者不相等时,需要更新该字段 + modifyFieldList.add(modifyTableParam); + continue; + } + // 7.验证是否可以为null(主键不参与是否为null的更新) + if (sysColumn.getIs_nullable().equals("NO") && !createTableParam.isFieldIsKey()) { + if (createTableParam.isFieldIsNull()) { + // 一个是可以一个是不可用,所以需要更新该字段 + modifyFieldList.add(modifyTableParam); + continue; + } + } else if (sysColumn.getIs_nullable().equals("YES") && !createTableParam.isFieldIsKey()) { + if (!createTableParam.isFieldIsNull()) { + // 一个是可以一个是不可用,所以需要更新该字段 + modifyFieldList.add(modifyTableParam); + continue; + } + } + // 8.验证注释 + if (!sysColumn.getColumn_comment().equals(createTableParam.getFieldComment())) { + modifyFieldList.add(modifyTableParam); + } + } + } + return modifyFieldList; + } + + /** + * 将allFieldList转换为Map结构 + * + * @param allFieldList + * @return + */ + private Map getAllFieldMap(List allFieldList) { + // 将fieldList转成Map类型,字段名作为主键 + Map fieldMap = new HashMap(); + for (Object obj : allFieldList) { + CreateTableParam createTableParam = (CreateTableParam) obj; + fieldMap.put(createTableParam.getFieldName(), createTableParam); + } + return fieldMap; + } + + /** + * 根据数据库中表的结构和model中表的结构对比找出删除的字段 + * + * @param table + * 表 + * @param columnNames + * 数据库中的结构 + * @param allFieldList + * model中的所有字段 + */ + private List getRemoveFieldList(Table table, List columnNames, List allFieldList) { + Map fieldMap = getAllFieldMap(allFieldList); + // 用于存删除的字段 + List removeFieldList = new ArrayList(); + for (String fieldNm : columnNames) { + // 判断该字段在新的model结构中是否存在 + if (fieldMap.get(fieldNm) == null) { + // 不存在,做删除处理 + removeFieldList.add(fieldNm); + } + } + return removeFieldList; + } + + /** + * 根据数据库中表的结构和model中表的结构对比找出新增的字段 + * + * @param table + * 表 + * @param allFieldList + * model中的所有字段 + * @param columnNames + * 数据库中的结构 + * @return 新增的字段 + */ + private List getAddFieldList(Table table, List allFieldList, List columnNames) { + List addFieldList = new ArrayList(); + for (Object obj : allFieldList) { + CreateTableParam createTableParam = (CreateTableParam) obj; + // 循环新的model中的字段,判断是否在数据库中已经存在 + if (!columnNames.contains(createTableParam.getFieldName())) { + // 不存在,表示要在数据库中增加该字段 + addFieldList.add(obj); + } + } + return addFieldList; + } + + /** + * 迭代出所有model的所有fields + * + * @param mySqlTypeAndLengthMap + * mysql数据类型和对应几个长度的map + * @param clas + * 准备做为创建表依据的class + * @return 表的全部字段 + */ + private List getAllFields(Map mySqlTypeAndLengthMap, Class clas) { + List fieldList = new ArrayList(); + Field[] fields = clas.getDeclaredFields(); + + // 判断是否有父类,如果有拉取父类的field,这里只支持多层继承 + fields = recursionParents(clas, fields); + + for (Field field : fields) { + // 判断方法中是否有指定注解类型的注解 + boolean hasAnnotation = field.isAnnotationPresent(Column.class); + if (hasAnnotation) { + // 根据注解类型返回方法的指定类型注解 + Column column = field.getAnnotation(Column.class); + CreateTableParam param = new CreateTableParam(); + param.setFieldName(column.name()); + param.setFieldType(column.type().toLowerCase()); + // TODO: bit 特殊处理,后期优化 + if("bit".equals(column.type())){ + if(column.length() >= 255){ + param.setFieldLength(1); + } + if(column.length() <= 64){ + param.setFieldLength(column.length()); + } + }else{ + param.setFieldLength(column.length()); + } + param.setFieldDecimalLength(column.decimalLength()); + // 主键时设置必须不为null + if (column.isKey()) { + param.setFieldIsNull(false); + } else { + param.setFieldIsNull(column.isNull()); + } + param.setFieldIsKey(column.isKey()); + param.setFieldIsAutoIncrement(column.isAutoIncrement()); + param.setFieldDefaultValue(column.defaultValue()); + param.setFieldComment(column.comment()); + int length = (Integer) mySqlTypeAndLengthMap.get(column.type().toLowerCase()); + param.setFileTypeLength(length); + // 获取当前字段的@Index注解 + Index index = field.getAnnotation(Index.class); + if (null != index) { + String[] indexValue = index.value(); + param.setFiledIndexName((index.name() == null || index.name().equals("")) + ? (Constants.IDX + ((indexValue.length == 0) ? column.name() : stringArrFormat(indexValue))) + : index.name()); + param.setFiledIndexValue( + indexValue.length == 0 ? Arrays.asList(column.name()) : Arrays.asList(indexValue)); + } + // 获取当前字段的@Unique注解 + Unique unique = field.getAnnotation(Unique.class); + if (null != unique) { + String[] uniqueValue = unique.value(); + param.setFiledUniqueName((unique.name() == null || unique.name().equals("")) + ? (Constants.UNI + + ((uniqueValue.length == 0) ? column.name() : stringArrFormat(uniqueValue))) + : unique.name()); + param.setFiledUniqueValue( + uniqueValue.length == 0 ? Arrays.asList(column.name()) : Arrays.asList(uniqueValue)); + } + fieldList.add(param); + } + } + return fieldList; + } + + /** + * String[] to format xxx_yyy_sss + * @param arr + * @return + */ + private String stringArrFormat(String[] arr) { + return String.valueOf(Arrays.toString(arr)).replaceAll(",", "_").replaceAll(" ", "").replace("[", "") + .replace("]", ""); + } + + /** + * 递归扫描父类的fields + * + * @param clas + * 类 + * @param fields + * 属性 + */ + @SuppressWarnings("rawtypes") + private Field[] recursionParents(Class clas, Field[] fields) { + if (clas.getSuperclass() != null) { + Class clsSup = clas.getSuperclass(); + List fieldList = new ArrayList(); + fieldList.addAll(Arrays.asList(fields)); + fieldList.addAll(Arrays.asList(clsSup.getDeclaredFields())); + fields = new Field[fieldList.size()]; + int i = 0; + for (Object field : fieldList.toArray()) { + fields[i] = (Field) field; + i++; + } + fields = recursionParents(clsSup, fields); + } + return fields; + } + + /** + * 根据传入的map创建或修改表结构 + * + * @param baseTableMap + * 操作sql的数据结构 + */ + private void createOrModifyTableConstruct(Map>> baseTableMap) { + // 1. 创建表 + createTableByMap(baseTableMap.get(Constants.NEW_TABLE_MAP)); + // 2. 删除要变更主键的表的原来的字段的主键 + dropFieldsKeyByMap(baseTableMap.get(Constants.DROPKEY_TABLE_MAP)); + // 3. 添加新的字段 + addFieldsByMap(baseTableMap.get(Constants.ADD_TABLE_MAP)); + // 4. 删除字段 + removeFieldsByMap(baseTableMap.get(Constants.REMOVE_TABLE_MAP)); + // 5. 修改字段类型等 + modifyFieldsByMap(baseTableMap.get(Constants.MODIFY_TABLE_MAP)); + // 6. 删除索引和约束 + dropIndexAndUniqueByMap(baseTableMap.get(Constants.DROPINDEXANDUNIQUE_TABLE_MAP)); + // 7. 创建索引 + addIndexByMap(baseTableMap.get(Constants.ADDINDEX_TABLE_MAP)); + // 8. 创建约束 + addUniqueByMap(baseTableMap.get(Constants.ADDUNIQUE_TABLE_MAP)); + + } + + /** + * 根据map结构删除索引和唯一约束 + * + * @param dropIndexAndUniqueMap + * 用于删除索引和唯一约束 + */ + private void dropIndexAndUniqueByMap(Map> dropIndexAndUniqueMap) { + if (dropIndexAndUniqueMap.size() > 0) { + for (Entry> entry : dropIndexAndUniqueMap.entrySet()) { + for (Object obj : entry.getValue()) { + Map map = new HashMap(); + map.put(entry.getKey(), obj); + log.info("开始删除表" + entry.getKey() + "中的索引" + obj); + createMysqlTablesMapper.dorpTabelIndex(map); + log.info("完成删除表" + entry.getKey() + "中的索引" + obj); + } + } + } + } + + /** + * 根据map结构创建索引 + * + * @param addIndexMap + * 用于创建索引和唯一约束 + */ + private void addIndexByMap(Map> addIndexMap) { + if (addIndexMap.size() > 0) { + for (Entry> entry : addIndexMap.entrySet()) { + for (Object obj : entry.getValue()) { + Map map = new HashMap(); + map.put(entry.getKey(), obj); + CreateTableParam fieldProperties = (CreateTableParam) obj; + if (null != fieldProperties.getFiledIndexName()) { + log.info("开始创建表" + entry.getKey() + "中的索引" + fieldProperties.getFiledIndexName()); + createMysqlTablesMapper.addTableIndex(map); + log.info("完成创建表" + entry.getKey() + "中的索引" + fieldProperties.getFiledIndexName()); + } + } + } + } + } + + /** + * 根据map结构创建唯一约束 + * + * @param addUniqueMap + * 用于创建索引和唯一约束 + */ + private void addUniqueByMap(Map> addUniqueMap) { + if (addUniqueMap.size() > 0) { + for (Entry> entry : addUniqueMap.entrySet()) { + for (Object obj : entry.getValue()) { + Map map = new HashMap(); + map.put(entry.getKey(), obj); + CreateTableParam fieldProperties = (CreateTableParam) obj; + if (null != fieldProperties.getFiledUniqueName()) { + log.info("开始创建表" + entry.getKey() + "中的唯一约束" + fieldProperties.getFiledUniqueName()); + createMysqlTablesMapper.addTableUnique(map); + log.info("完成创建表" + entry.getKey() + "中的唯一约束" + fieldProperties.getFiledUniqueName()); + } + } + } + } + } + + /** + * 根据map结构修改表中的字段类型等 + * + * @param modifyTableMap + * 用于存需要更新字段类型等的表名+结构 + */ + private void modifyFieldsByMap(Map> modifyTableMap) { + // 做修改字段操作 + if (modifyTableMap.size() > 0) { + for (Entry> entry : modifyTableMap.entrySet()) { + for (Object obj : entry.getValue()) { + Map map = new HashMap(); + map.put(entry.getKey(), obj); + CreateTableParam fieldProperties = (CreateTableParam) obj; + log.info("开始修改表" + entry.getKey() + "中的字段" + fieldProperties.getFieldName()); + createMysqlTablesMapper.modifyTableField(map); + log.info("完成修改表" + entry.getKey() + "中的字段" + fieldProperties.getFieldName()); + } + } + } + } + + /** + * 根据map结构删除表中的字段 + * + * @param removeTableMap + * 用于存需要删除字段的表名+结构 + */ + private void removeFieldsByMap(Map> removeTableMap) { + // 做删除字段操作 + if (removeTableMap.size() > 0) { + for (Entry> entry : removeTableMap.entrySet()) { + for (Object obj : entry.getValue()) { + Map map = new HashMap(); + map.put(entry.getKey(), obj); + String fieldName = (String) obj; + log.info("开始删除表" + entry.getKey() + "中的字段" + fieldName); + createMysqlTablesMapper.removeTableField(map); + log.info("完成删除表" + entry.getKey() + "中的字段" + fieldName); + } + } + } + } + + /** + * 根据map结构对表中添加新的字段 + * + * @param addTableMap + * 用于存需要增加字段的表名+结构 + */ + private void addFieldsByMap(Map> addTableMap) { + // 做增加字段操作 + if (addTableMap.size() > 0) { + for (Entry> entry : addTableMap.entrySet()) { + for (Object obj : entry.getValue()) { + Map map = new HashMap(); + map.put(entry.getKey(), obj); + CreateTableParam fieldProperties = (CreateTableParam) obj; + log.info("开始为表" + entry.getKey() + "增加字段" + fieldProperties.getFieldName()); + createMysqlTablesMapper.addTableField(map); + log.info("完成为表" + entry.getKey() + "增加字段" + fieldProperties.getFieldName()); + } + } + } + } + + /** + * 根据map结构删除要变更表中字段的主键 + * + * @param dropKeyTableMap + * 用于存需要删除主键的表名+结构 + */ + private void dropFieldsKeyByMap(Map> dropKeyTableMap) { + // 先去做删除主键的操作,这步操作必须在增加和修改字段之前! + if (dropKeyTableMap.size() > 0) { + for (Entry> entry : dropKeyTableMap.entrySet()) { + for (Object obj : entry.getValue()) { + Map map = new HashMap(); + map.put(entry.getKey(), obj); + CreateTableParam fieldProperties = (CreateTableParam) obj; + log.info("开始为表" + entry.getKey() + "删除主键" + fieldProperties.getFieldName()); + createMysqlTablesMapper.dropKeyTableField(map); + log.info("完成为表" + entry.getKey() + "删除主键" + fieldProperties.getFieldName()); + } + } + } + } + + /** + * 根据map结构创建表 + * + * @param newTableMap + * 用于存需要创建的表名+结构 + */ + private void createTableByMap(Map> newTableMap) { + // 做创建表操作 + if (newTableMap.size() > 0) { + for (Entry> entry : newTableMap.entrySet()) { + Map> map = new HashMap>(); + map.put(entry.getKey(), entry.getValue()); + log.info("开始创建表:" + entry.getKey()); + createMysqlTablesMapper.createTable(map); + log.info("完成创建表:" + entry.getKey()); + } + } + } + + /** + * 获取Mysql的类型,以及类型需要设置几个长度,这里构建成map的样式 + * 构建Map(字段名(小写),需要设置几个长度(0表示不需要设置,1表示需要设置一个,2表示需要设置两个)) + */ + public static Map mySqlTypeAndLengthMap() { + Field[] fields = MySqlTypeConstant.class.getDeclaredFields(); + Map map = new HashMap(); + for (Field field : fields) { + LengthCount lengthCount = field.getAnnotation(LengthCount.class); + map.put(field.getName().toLowerCase(), lengthCount.LengthCount()); + } + return map; + } } diff --git a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/mapping/system/CreateMysqlTablesMapper.xml b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/mapping/system/CreateMysqlTablesMapper.xml index 35f7583a9ff11ea1c832a275f0d4eee077082268..10016044cebef5cc39d9101b67f1f7664b99df73 100644 --- a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/mapping/system/CreateMysqlTablesMapper.xml +++ b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/mapping/system/CreateMysqlTablesMapper.xml @@ -48,12 +48,44 @@ - DEFAULT #{fields.fieldDefaultValue} + + + DEFAULT 1 + + + DEFAULT 0 + + + DEFAULT 1 + + + DEFAULT 0 + + + + DEFAULT #{fields.fieldDefaultValue} + - DEFAULT #{fields.fieldDefaultValue} + + + DEFAULT 1 + + + DEFAULT 0 + + + DEFAULT 1 + + + DEFAULT 0 + + + + DEFAULT #{fields.fieldDefaultValue} + COMMENT #{fields.fieldComment} diff --git a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/utils/ClassTools.java b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/utils/ClassTools.java index 1d59074daa0486c8b9ff911765721c5bc6f5d7db..55b01a19fcdbc0052d68ecc61358253e23ba37d7 100644 --- a/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/utils/ClassTools.java +++ b/mybatis-enhance-actable/src/main/java/com/gitee/sunchenbin/mybatis/actable/utils/ClassTools.java @@ -27,91 +27,94 @@ public class ClassTools{ /** * 从包package中获取所有的Class * - * @param pack 扫描的包 + * @param packs 扫描的包,支持多个","或者";"隔开 * @return 该包下的class */ - public static Set> getClasses(String pack){ - + public static Set> getClasses(String packs){ + // 第一个class类的集合 Set> classes = new LinkedHashSet>(); - // 是否循环迭代 - boolean recursive = true; - // 获取包的名字 并进行替换 - String packageName = pack; - String packageDirName = packageName.replace('.', '/'); - // 定义一个枚举的集合 并进行循环来处理这个目录下的things - Enumeration dirs; - try{ - dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); - // 循环迭代下去 - while (dirs.hasMoreElements()){ - // 获取下一个元素 - URL url = dirs.nextElement(); - // 得到协议的名称 - String protocol = url.getProtocol(); - // 如果是以文件的形式保存在服务器上 - if ("file".equals(protocol)) { - System.err.println("file类型的扫描"); - // 获取包的物理路径 - String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); - // 以文件的方式扫描整个包下的文件 并添加到集合中 - findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); - }else if ("jar".equals(protocol)) { - // 如果是jar包文件 - // 定义一个JarFile - System.err.println("jar类型的扫描"); - JarFile jar; - try{ - // 获取jar - jar = ((JarURLConnection) url.openConnection()).getJarFile(); - // 从此jar包 得到一个枚举类 - Enumeration entries = jar.entries(); - // 同样的进行循环迭代 - while (entries.hasMoreElements()){ - // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 - JarEntry entry = entries.nextElement(); - String name = entry.getName(); - // 如果是以/开头的 - if (name.charAt(0) == '/') { - // 获取后面的字符串 - name = name.substring(1); - } - // 如果前半部分和定义的包名相同 - if (name.startsWith(packageDirName)) { - int idx = name.lastIndexOf('/'); - // 如果以"/"结尾 是一个包 - if (idx != -1) { - // 获取包名 把"/"替换成"." - packageName = name.substring(0, idx).replace('/', '.'); + // 拆成多个pack,支持多个 + String[] split = packs.split(",|;"); + for (String pack : split) { + // 是否循环迭代 + boolean recursive = true; + // 获取包的名字 并进行替换 + String packageName = pack; + String packageDirName = packageName.replace('.', '/'); + // 定义一个枚举的集合 并进行循环来处理这个目录下的things + Enumeration dirs; + try{ + dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); + // 循环迭代下去 + while (dirs.hasMoreElements()){ + // 获取下一个元素 + URL url = dirs.nextElement(); + // 得到协议的名称 + String protocol = url.getProtocol(); + // 如果是以文件的形式保存在服务器上 + if ("file".equals(protocol)) { + System.err.println("file类型的扫描:" + pack); + // 获取包的物理路径 + String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); + // 以文件的方式扫描整个包下的文件 并添加到集合中 + findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); + }else if ("jar".equals(protocol)) { + // 如果是jar包文件 + // 定义一个JarFile + System.err.println("jar类型的扫描"); + JarFile jar; + try{ + // 获取jar + jar = ((JarURLConnection) url.openConnection()).getJarFile(); + // 从此jar包 得到一个枚举类 + Enumeration entries = jar.entries(); + // 同样的进行循环迭代 + while (entries.hasMoreElements()){ + // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + // 如果是以/开头的 + if (name.charAt(0) == '/') { + // 获取后面的字符串 + name = name.substring(1); } - // 如果可以迭代下去 并且是一个包 - if ((idx != -1) || recursive) { - // 如果是一个.class文件 而且不是目录 - if (name.endsWith(".class") && !entry.isDirectory()) { - // 去掉后面的".class" 获取真正的类名 - String className = name.substring(packageName.length() + 1, name.length() - 6); - try{ - // 添加到classes - classes.add(Class.forName(packageName + '.' + className)); - }catch (ClassNotFoundException e){ - // log - // .error("添加用户自定义视图类错误 找不到此类的.class文件"); - e.printStackTrace(); + // 如果前半部分和定义的包名相同 + if (name.startsWith(packageDirName)) { + int idx = name.lastIndexOf('/'); + // 如果以"/"结尾 是一个包 + if (idx != -1) { + // 获取包名 把"/"替换成"." + packageName = name.substring(0, idx).replace('/', '.'); + } + // 如果可以迭代下去 并且是一个包 + if ((idx != -1) || recursive) { + // 如果是一个.class文件 而且不是目录 + if (name.endsWith(".class") && !entry.isDirectory()) { + // 去掉后面的".class" 获取真正的类名 + String className = name.substring(packageName.length() + 1, name.length() - 6); + try{ + // 添加到classes + classes.add(Class.forName(packageName + '.' + className)); + }catch (ClassNotFoundException e){ + // log + // .error("添加用户自定义视图类错误 找不到此类的.class文件"); + e.printStackTrace(); + } } } } } + }catch (IOException e){ + // log.error("在扫描用户定义视图时从jar包获取文件出错"); + e.printStackTrace(); } - }catch (IOException e){ - // log.error("在扫描用户定义视图时从jar包获取文件出错"); - e.printStackTrace(); } } + }catch (IOException e){ + e.printStackTrace(); } - }catch (IOException e){ - e.printStackTrace(); } - return classes; }