摘要:注解是棄用不再使用的意思。是處理源碼級別的注解,它會生成新的字節碼或其它文件。這個該怎么實現呢我們需要在啟動之后文件轉為字節碼文件之前,就需要生成對應的和方法,因為它只在編譯期有效。我們在啟動后,會根據注解,來創建相應的數據表。
導讀
模擬hibernate的注解,創建數據表的源碼地址:https://gitee.com/zbone/myanno
注解釋義java開發人員對注解,應該不會很陌生。我們在開發的過程中,經常會用到注解,那么,什么是注解呢?
注解,也被稱為元數據,為我們在代碼中添加信息,提供了一種形式化的方法,使我們在稍后某個時刻,可以非常方便地使用這些原數據(thinking in java)。
這句話是什么意思?舉一個hibernate的@Table注解,我們在實體類上定義該注解,它不會立即生效。我們啟動Tomcat時,并借助spring工具,便觸發該注解,從而創建數據表。也就是說,我們先定義注解,等到合適的時間,我們在使用該注解。
注解定義我們定義注解非常簡單,只要在interface前加上@符號,這就是一個注解了,如代碼所示:
package com.zbystudy.anno; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * Created By zby on 13:45 2019/3/31 * 表名注解 */ @Target(TYPE) @Retention(RUNTIME) public @interface Table { /** * 表名 */ String name() default ""; /** * 表屬于哪個數據庫 */ String catelog() default ""; }
這類似于hibernate中的@Table注解,光定義注解是沒有意義的。它要配合注解處理器才能生效,我會在下文提到如何寫注解處理器。
內置三大注解 override注解我們在開發的過程中,經常會用到override注解,如代碼所示: @Override public Result> listAccountById(Long memberId)
;該注解表示當前方法將覆蓋超類中的方法。
阿里巴巴規范要求,如果當前方法覆蓋超類的方法,必須寫上override注解。因為我們如果不小心拼寫錯誤,或者方法簽名對不上覆蓋的方法,編譯器就會發出錯誤地提示。我們如果忘記寫override,但這并不影響使用。比如,我們需要重寫接口AccountService的auditAcct方法簽名,但并沒有加上override注解,編譯器就會錯誤的提示,但不是報錯。
java除了override內置注解,還有Deprecated注解和SuppressWarnings注解。
Deprecated注解Deprecated是棄用、不再使用的意思。我們如果用其來修飾類、方法、屬性、構造器等,當我們去調用被其修飾的類、方法、屬性、構造器后,編輯器就會發出警告信息。我此時有一個ArrayUtil類,其中有個方法,判斷數組是否為空數組,如代碼所示:
public class ArrayUtil { /** * Created By zby on 20:50 2019/2/13 * 判斷字符串是否為null */ @Deprecated public static boolean isEmpty(Object[] objects) { if (null == objects || objects.length == 0) return true; for (Object object : objects) { if (null != object) return false; } return true; } public static void main(String[] args) { String[] str={"nihao","wohao"}; boolean isEmpty=ArrayUtil.isEmpty(str); System.out.println(isEmpty); } }
這也不影響使用,只是編輯器不建議你使用了,如圖所示:
SuppressWarnings注解SuppressWarnings拆開來看,Suppress是壓制的意思,Warnings是警告的意思。它表示為壓制警告,即關閉不當的警告信息。比如,還是上面的ArrayUtil類,其中有一個containsAny(String param, String[] filters)方法,它表示數組中是否包含某個值,但目前沒有用到,其報出這個提示:
我們在上面的方法添加SuppressWarnings注解,其便不會報出這樣的警告信息:
/** * Created By zby on 15:00 2019/2/14 * 判斷參數是否在該數組中 * * @param param 參數 * @param filters 數組 */ @SuppressWarnings("all") public static boolean containsAny(String param, String[] filters) { if (isNotEmpty(filters)) { if (StringUtils.isNotBlank(param)) { for (String tmp : filters) { if (tmp.equals(param)) { return true; } } } } return false; }
以上三個注解是java內置的三大注解,Override和SuppressWarnings是源碼級別(RetentionPolicy.SOURCE)的注解,而Deprecated是運行時(RetentionPolicy.RUNTIME)注解。源碼級別和和運行時有什么區別,這個會在下文中講解。
同時,除了以上三個內置注解,java還提供了四種自定義注解的注解,分別是@Target、@Retention、@Documented、@Inherited,這四種注解是我寫這篇文檔的重點,在下面便講解這四種注解,也成為元注解。
元注解元注解幫助我們自定義注解,它本身包含四種注解,以下是四種注解的作用域:
SOURCE和RUNTIME的區別正如我們上文提到的,光定義注解是完全沒有意義,我們需要手動書寫注解的處理器。
SOURCESOURCE是處理源碼級別的注解,它會生成新的字節碼或其它文件。比如說,我們現在有一個javabean文件,我們想通過一個注解來自動生成set和get方法。這個該怎么實現呢?我們需要在啟動jvm之后、.java文件轉為字節碼(.class)文件之前,就需要生成對應的set和get方法,因為它只在JVM編譯期有效。
我們事先定義好注解類,其是將屬性生成set和get方法,如下代碼所示
/** * Created By zby on 14:35 2019/4/11 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Property { }
這里,有一個項目的java類,如代碼所示:
public class Project { @Property private String name; }
我們這樣定義之后,預想在啟動了jvm后,生成如下.class文件
public class Project { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } }
事實上,不會生成這樣的.class文件,為什么呢?生成這樣的.class文件超出了注解的功能,它需要再寫其它的工具類來實現。因為我們定義好注解后,需要手動去寫注解的處理器,lombok就是這樣實現的。關于,如何書寫SOURCE的注解處理器,你可以參考這個文檔:插件化注解處理API(Pluggable Annotation Processing API)
如果你的實體類上寫了lombok注解后,你可以去看看當前java文件所對應的.class文件,其和java原文件是完全的不同。
RUNTIMERUNTIME不會生成新的文件,它只是在運行時,處理程序中的某些功能,比如hibernate的注解。我們在啟動jvm后,hibernate會根據注解,來創建相應的數據表。但是,我們定義好注解,也是沒有任何意義的。注解本身不會執行任何操作,所有的操作都是在我們定義的處理器中執行。
現在有一個注解類,標記字段是否是主鍵:
/** * Created By zby on 23:17 2019/4/1 * 自增主鍵 */ @Target({METHOD,FIELD}) @Retention(RUNTIME) public @interface Id { }
它本身沒有任何意義,我們可以通過反射獲取注解,從而執行相應的操作。
在下文中,我會模擬hibernate的注解,來創建生成數據表的代碼,源碼的地址在:https://gitee.com/zbone/myanno
注解創建SQL表 注意事項我們既然想通過注解創建SQL語句,我們必須非常熟悉SQL語句。我們現在想用純SQL語句來創建表,以及表之間的外鍵關系。我們必須在創建外鍵表之前創建主表,否則,SQL會報出相應的錯誤。
我們拿到了實體類,如何區分主表和外鍵表,設置主表和外鍵的先后關系,即先創建主表,再創建外表。
怎么將java類型轉為SQL類型?
對于columnDefinition方法來說,如果其存儲了當前字段自定義的類型長度,而java類型轉化為SQL類型時,默認是字段類型的最大值。如果該類型長度小于默認的字段類型,我們怎么拿到自定義類型?
。。。。。。
原生SQL語句-- 如果學生表存在,即刪除學生表 DROP TABLE if EXISTS tb_student ; -- 創建學生表 CREATE TABLE `tb_student` ( `id` INT (11) NOT NULL AUTO_INCREMENT COMMENT "主鍵" PRIMARY KEY, `name` VARCHAR (255) COMMENT "姓名" NULL, `sex` bit (1) COMMENT "性別" NULL ) ENGINE = INNODB DEFAULT CHARACTER SET = utf8; -- 如果課程表存在,則刪除課程表 DROP TABLE if EXISTS tb_course ; -- 創建課程表 CREATE TABLE `tb_course` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT "自增主鍵" PRIMARY KEY, `score` double NULL DEFAULT 0 COMMENT "分數" , `subject_name` enum("SUBJECT_TYPE_CHINESE","SUBJECT_TYPE_MATH","SUBJECT_TYPE_ENGLISH") NULL COMMENT "課程名稱,存儲枚舉key值" , `teacher_name` varchar(255) NULL DEFAULT NULL COMMENT "老師名稱" , `student_id` int(11) NOT NULL, CONSTRAINT fk_student_course FOREIGN KEY (`student_id`) REFERENCES `tb_student` (`id`) ON DELETE CASCADE ON UPDATE CASCADE )ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; -- 如果分數表存在,則刪除分數表 DROP TABLE if EXISTS tb_score ; -- 創建分數表 CREATE TABLE `tb_score`( id BIGINT(20) not null AUTO_INCREMENT comment "主鍵" primary key, score DOUBLE null , course_id BIGINT(18) null COMMENT "外鍵是課程表" , CONSTRAINT fk_course_score FOREIGN KEY(`course_id`) REFERENCES `tb_course`(`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8
以上是創建tb_student表和tb_course表的原生SQL語句,我們就是通過注解來拼裝原生mysql語句,并調用jdbc的方法來創建數據表。以上的SQL語句也是通過我寫的這個框架代碼生成的
定義注解定義的注解太多了,我在這里,只選擇Column注解展示,其它注解等你們下載好源碼,自己可以選擇性地看。
/** * Created By zby on 14:07 2019/3/31 * 表名類型的信息 */ @Target({FIELD,METHOD}) @Retention(RUNTIME) public @interface Column { /** * 字段名 */ String name() default ""; /** * 字符長度 */ int length() default 255; /** * 字段是否為空 */ boolean nullable() default true; /** * 是否是唯一值 */ boolean unique() default false; /** * 字段類型定義 */ String columnDefinition() default ""; }設置權重
在將javabean對象和對象屬性轉化為SQL語句之前,我將javabean對象設置了權重。權重用來區分將javabean對象生成SQL語句的先后順序,避免在未創建主表就創建了外鍵表的錯誤。因而,對類集合進行處理排序,也就是根據權重的先后順序。
這里使用到的是LinkedHashSet類,其存儲的是Javaban的類名。我沒有使用HashSet類,因為鏈表具有先后順序,而散列表沒有先后順序。同時,這里使用了遞歸,因為,當前外鍵表可能是別的外鍵表的主表,這時,就需要再次遍歷,如核心代碼所示:
/** * Created By zby on 23:22 2019/4/2 * 設置權重 * * @param entityPath 實體類的路徑 * @param priority 權重值 * @param fieldTypeName 屬性類型名稱 */ private static void setPriority(String entityPath, Integer priority, String fieldTypeName) { try { Class clazz = Class.forName(isNotBlank(entityPath) ? entityPath : fieldTypeName); Field[] fields = clazz.getDeclaredFields(); if (ArrayUtil.isNotEmpty(fields)) { boolean hasManyToOne = false; for (Field field : fields) { ManyToOne manyToOne = field.getDeclaredAnnotation(ManyToOne.class); if (null != manyToOne) { fieldTypeName = field.getType().getName(); hasManyToOne = true; break; } } if (hasManyToOne) { setPriority(null, ++priority, fieldTypeName); } else { entityPathMap.put(ENTITY_PATH, priority); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } }
你會看到上面是私有方法,我這邊只對外開放一個方法,對外開放的是設置權重后的實體類的類路徑名:
/** * Created By zby on 23:31 2019/4/2 * 獲取排序后的值 */ public static Set生成SQL語句sortEntityPath() { int maxPriority = getMaxPriority(); Set entityPathSet = new LinkedHashSet<>(); for (int i = 1; i <= maxPriority; i++) { if (entityPathMap != null && entityPathMap.size() > 0) { for (Map.Entry entry : entityPathMap.entrySet()) { int value = entry.getValue(); if (value == i) { entityPathSet.add(entry.getKey()); } } } } return entityPathSet; }
經過上面的排序之后,開始生成創建表的SQL語句,這里面就要用到了反射。因為是運行時注解類型,可以使用反射來獲取對象類型。
我們將創建好的SQL語句放置在map中,key值是表名,value值是SQL語句,但是,我們使用的是 private static Map
以下是生成SQL語句的代碼,可能有點長,細心看,還是能看完的哈:
static { SetentityFileNames = EntityPriority.sortEntityPath(); // 創建類 for (String entityFileName : entityFileNames) { try { Class clazz = Class.forName(entityFileName); // 獲取表名 Table dbTable = (Table) clazz.getDeclaredAnnotation(Table.class); if (null == dbTable) { System.out.println("no table annotation found in class:" + entityFileName); continue; } String tableName = dbTable.name(); String currClazzName = clazz.getSimpleName(); if (isBlank(tableName)) { System.out.println("no table name found in class:" + entityFileName); continue; } StringBuilder tableBuilder = new StringBuilder("CREATE TABLE" + BLANK_OP + BACK_QUOTE + tableName + BACK_QUOTE + LEFT_BRACKET + LINE_FEED_OP); // 設置外鍵信息 // 存儲的是外鍵信息 Set foreignTables = new LinkedHashSet<>(); // 獲取屬性 Field[] fields = clazz.getDeclaredFields(); if (isNotEmpty(fields)) { for (Field field : fields) { // 設置初始值 TableColumn tableColumn = ColumnBuilder.instance(); Column column = field.getDeclaredAnnotation(Column.class); if (null != column) { // 獲取字段名 tableColumn.setColName(isBlank(column.name()) ? field.getName() : column.name()); tableColumn.setColLength(column.length()); tableColumn.setColDefinition(column.columnDefinition()); tableColumn.setColNull(column.nullable() ? NULL : NOT_NULL); tableColumn.setColUnique(column.unique() ? "unique" : NUL_OP); } // 主鍵 Id id = field.getDeclaredAnnotation(Id.class); if (null != id) { tableColumn.setColPk("primary key"); tableColumn.setColPkVal(tableColumn.getColName()); } // 自動增長 GeneratedValue generated = field.getDeclaredAnnotation(GeneratedValue.class); if (null != generated) { tableColumn.setIncrementStrategy(generated.strategy().name().equals("AUTO") ? "AUTO_INCREMENT" : NUL_OP); } // 獲取屬性類型,同時獲取字段長度 String typeName = field.getType().getSimpleName(); tableColumn.setColType(TypeTransformer.javaToSql(typeName)); // 處理枚舉類型 Enumerated enumerated = field.getDeclaredAnnotation(Enumerated.class); if (null != enumerated) { String enumKey = NUL_OP; if ("STRING".equals(enumerated.value().name())) { for (Object obj : field.getType().getEnumConstants()) { enumKey += SINGLE_QUOTES + obj.toString() + SINGLE_QUOTES + SERIES_COMMA_OP; } } else if ("ORDINAL".equals(enumerated.value().name())) { Object[] objects = field.getType().getEnumConstants(); for (int i = 0; i < objects.length; i++) { enumKey += SINGLE_QUOTES + i + SINGLE_QUOTES + SERIES_COMMA_OP; } } else { continue; } enumKey = substring(enumKey, 0, enumKey.lastIndexOf(SERIES_COMMA_OP)); tableColumn.setColType("enum" + LEFT_BRACKET + enumKey + RIGHT_BRACKET); } // 處理多對一的關系 ManyToOne manyToOne = field.getDeclaredAnnotation(ManyToOne.class); if (manyToOne != null) { CascadeType[] cascadeTypes = manyToOne.cascade(); if (ArrayUtil.isEmpty(cascadeTypes)) { tableColumn.setColCascade("ON DELETE CASCADE ON UPDATE CASCADE"); } else if (ArrayUtil.isNotEmpty(cascadeTypes) && cascadeTypes.length == 1) { tableColumn.setColCascade("ON DELETE " + transNoAction(cascadeTypes[0].name()) + " ON UPDATE CASCADE"); } else if (ArrayUtil.isNotEmpty(cascadeTypes) && cascadeTypes.length == 2) { tableColumn.setColCascade("ON DELETE " + transNoAction(cascadeTypes[0].name()) + " ON UPDATE " + transNoAction(cascadeTypes[1].name())); } else { continue; } } // 關聯表 JoinColumn joinColumn = field.getDeclaredAnnotation(JoinColumn.class); if (null != joinColumn) { ForeignTable foreignTable = ForeignTableBuilder.instance(); foreignTable.setForeignKeyName(joinColumn.name()); foreignTable.setCascade(tableColumn.getColCascade()); foreignTable.setForeignTableName(joinColumn.table()); tableColumn.setColName(joinColumn.name()); tableColumn.setForeignTable(joinColumn.table()); tableColumn.setColDefinition(joinColumn.columnDefinition()); tableColumn.setColNull(joinColumn.nullable() ? NULL : NOT_NULL); // 外鍵類型類型忘記填寫 Class fieldType = Class.forName(field.getType().getName()); if (isBlank(tableColumn.getForeignTable())) { dbTable = (Table) fieldType.getDeclaredAnnotation(Table.class); if (dbTable != null && isNotBlank(dbTable.name())) { tableColumn.setForeignTable(dbTable.name()); foreignTable.setForeignTableName(tableColumn.getForeignTable()); } } foreignTable.setForeignName("fk" + UNDERLINE + classNameToProName(fieldType.getSimpleName()) + UNDERLINE + classNameToProName(currClazzName)); fields = fieldType.getDeclaredFields(); if (ArrayUtil.isNotEmpty(fields)) { for (Field fkField : fields) { id = fkField.getDeclaredAnnotation(Id.class); if (null != id) { tableColumn.setColType(TypeTransformer.javaToSql(fkField.getType().getSimpleName())); column = fkField.getDeclaredAnnotation(Column.class); // 設置外鍵表的關聯字段 foreignTable.setForeignTablePk(null != column && isBlank(column.name()) ? column.name() : fkField.getName()); } } } foreignTables.add(foreignTable); } // 處理columnDefinition = "int(11) NOT NULL COMMENT "外鍵是學生表""和真實的字段 String colType = tableColumn.getColType(); String colDefinition = tableColumn.getColDefinition(); if (isNotBlank(colDefinition) && isNotBlank(colType)) { String[] sqlNumberType = {"INT(11)", "INTEGER(11)", "BIGINT(20)", "VARCHAR(255)", "SMALLINT(6)", "NUMERIC(10)", "TINYINT(4)", "BIT(1)"}; if (ArrayUtils.contains(sqlNumberType, colType)) { int colNum = Integer.parseInt(substring(colType, colType.indexOf(LEFT_BRACKET) + 1, colType.lastIndexOf(RIGHT_BRACKET))); colType = substring(colType, 0, colType.lastIndexOf(LEFT_BRACKET)); if (StringUtils.containsAny(colDefinition, colType)) { int typeEndLength = StringHelper.getEndLength(colDefinition, colType); // COL_DEFINITION包含字符串,且同時包含( ) if (typeEndLength != 0 && StringUtils.containsAny(colDefinition, LEFT_BRACKET, RIGHT_BRACKET)) { String definitionNum = StringUtils.substring(colDefinition, typeEndLength + 1, colDefinition.indexOf(")")); if (Integer.parseInt(definitionNum) <= colNum) { tableColumn.setColType(colType + LEFT_BRACKET + Integer.parseInt(definitionNum) + RIGHT_BRACKET); } tableColumn.setColDefinition(StringUtils.remove(colDefinition, tableColumn.getColType())); } } } } tableBuilder.append(TAB_OP + BLANK_OP + tableColumn.getColName() + BLANK_OP + tableColumn.getColType() + BLANK_OP + tableColumn.getColNull() + BLANK_OP + (tableColumn.getIncrementStrategy() != null ? tableColumn.getIncrementStrategy() + BLANK_OP : NUL_OP) + (tableColumn.getColDefinition() != null ? tableColumn.getColDefinition() + BLANK_OP : NUL_OP) + (tableColumn.getColPk() != null ? tableColumn.getColPk() : NUL_OP) + SERIES_COMMA_OP + LINE_FEED_OP ); } } if (foreignTables.size() > 0) { for (ForeignTable foreignTable : foreignTables) { tableBuilder.append(TAB_OP + BLANK_OP + "CONSTRAINT" + BLANK_OP + foreignTable.getForeignName() + BLANK_OP + "FOREIGN KEY" + LEFT_BRACKET + BACK_QUOTE + foreignTable.getForeignKeyName() + BACK_QUOTE + RIGHT_BRACKET + BLANK_OP + "REFERENCES" + BLANK_OP + BACK_QUOTE + foreignTable.getForeignTableName() + BACK_QUOTE + LEFT_BRACKET + BACK_QUOTE + foreignTable.getForeignTablePk() + BACK_QUOTE + RIGHT_BRACKET + BLANK_OP + foreignTable.getCascade() + SERIES_COMMA_OP + LINE_FEED_OP ); } } tableBuilder = new StringBuilder(tableBuilder.substring(0, tableBuilder.lastIndexOf(SERIES_COMMA_OP))); tableBuilder.append(LINE_FEED_OP + BLANK_OP + RIGHT_BRACKET + BLANK_OP + EngineCharator.tablEengine + BLANK_OP + EngineCharator.tableCharacter); if (MapUtil.existKey(tableName, dbTableMap)) { continue; } dbTableMap.put(tableName, tableBuilder); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
上面的代碼有點冗長,沒有做具體的細分,以后,會慢慢地優化。
生成數據表經過上面的步驟后,此時,獲得了數據表的SQL語句,開始調用jdbc底層的代碼。在執行創建數據表的代碼之前,還需要些配置文件:
#實體類的路徑,可以采用ant風格 jdbc.package=com.zbystudy.po.* #是否忽略已創建表,如果為true,不刪除已創建的表 #如果為false,則刪除所有的表,重新創建新表 jdbc.ignoreExistTable=false jdbc.driver=com.mysql.jdbc.Driver jdbc.username=root jdbc.password=root jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&useSSL=true
有了配置文件,根據配置文件的條件,來創建數據表和刪除數據表。根據先創建主表、再創建外鍵表的順來創建表,根據先刪除外鍵表、再刪除主表的方式來刪除數據表。
/** * Created By zby on 16:12 2019/4/3 * 創建表 */ public class CreationTable { /** * Created By zby on 16:15 2019/4/3 * 判斷表是否存在 */ public static boolean existsTable(String tableName) { if (isBlank(tableName)) { return false; } String sql = "SELECT column_name FROM information_schema.columns WHERE table_name=?"; Connection conn = sqlConnectFactory.createConnect(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql); ps.setString(1, tableName); rs = ps.executeQuery(); while (rs.next()) { return true; } return false; } catch (SQLException e) { e.printStackTrace(); } finally { ConnCloseUtil.closeConn(conn, ps, rs); } return false; } /** * Created By zby on 10:48 2019/4/8 * 刪除表 */ public static boolean dropTable(String tableName) { if (isBlank(tableName)) { return false; } String sql = "DROP TABLE " + tableName; Connection conn = sqlConnectFactory.createConnect(); PreparedStatement ps = null; try { ps = conn.prepareStatement(sql); int result = ps.executeUpdate(); return result == 0 ? true : false; } catch (SQLException e) { e.printStackTrace(); } finally { ConnCloseUtil.closeConn(conn, ps); } return false; } /** * Created By zby on 0:12 2019/4/9 *測試創建表* 批量刪除 */ public static void batchDropTable(Map
tableSqls) { if (isKeyBlank(tableSqls)) { throw new RuntimeException("表名為空,請核查后再刪除表"); } for (Map.Entry entry : tableSqls.entrySet()) { String tableName = entry.getKey(); // 表不存在,跳過此循環 if (!existsTable(tableName)) { continue; } dropTable(entry.getKey()); } } /** * Created By zby on 9:30 2019/4/8 * 創建數據表 */ public synchronized static boolean batchCreateTable() { // 是否忽略已存在的表 String ignoreExistTable = sqlConnectFactory.getProperties().getProperty("jdbc.ignoreExistTable"); Connection conn = sqlConnectFactory.createConnect(); boolean tranSuccess = false; try { conn.setAutoCommit(false); conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); Map tableSQLs = TableInit.getDbTableMap(); if (isNotBlank(ignoreExistTable) && ignoreExistTable.equalsIgnoreCase("true")) { // 如果表名為空,則無法刪除 if (isKeyBlank(tableSQLs)) { return false; } for (Map.Entry entry : tableSQLs.entrySet()) { boolean tableExists = existsTable(entry.getKey()); // 如果表存在,則跳過循環 if (tableExists) { continue; } tranSuccess = CreateTable(entry.getKey(), conn, entry.getValue().toString()); } } else { // map數據反轉 Map reverseTableSqls = reverseMap(tableSQLs); // 如果表名為空,則無法刪除 if (isKeyBlank(reverseTableSqls)) { return false; } // 先刪除所有表,在創建表 batchDropTable(reverseTableSqls); for (Map.Entry entry : tableSQLs.entrySet()) { tranSuccess = CreateTable(entry.getKey(), conn, entry.getValue().toString()); } } if (tranSuccess) { conn.commit(); } } catch (SQLException e) { e.printStackTrace(); } finally { ConnCloseUtil.closeConn(conn); } return tranSuccess; } /** * Created By zby on 9:30 2019/4/9 * 創建數據表 * * @param tableName 表名 * @param conn 數據庫連接對象 * @param sql 創建表的執行語句 */ public static boolean CreateTable(String tableName, Connection conn, String sql) { if (conn != null && isNotBlank(sql)) { PreparedStatement ps = null; try { ps = conn.prepareStatement(sql); return ps.executeUpdate() == 0 ? true : false; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("添加表tableName=" + tableName + "失敗"); } finally { ConnCloseUtil.closeState(ps); } } return false; } }
萬事俱備,只欠東風,既然寫好了代碼,那么,就測試能不能創建成功,以下是測試代碼:
package com.zbystudy; import com.zbystudy.core.vo.CreationTable; import org.junit.Test; /** * Created By zby on 11:32 2019/4/9 */ public class CreationTableTest { @Test public void test(){ boolean tracSucc = CreationTable.batchCreateTable(); if (tracSucc) { System.out.println("創建數據表成功"); } else { System.out.println("創建數據表失敗"); } } }
輸出結果如圖所示:
查看數據庫,發現有生成的數據表,表示是真的生成了數據表:
總結通過模擬hibernate框架,確實學到了不少東西,可能這就是成長吧。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77535.html
摘要:面向切面的本章主要內容面向切面編程的基本原理通過創建切面使用注解為切面注入依賴。什么是面向切面編程切面能夠幫我們模塊化橫切關注點。在使用面向切面編程時,我們仍然在一個地方定義通知功能,而無需修改受影響的類。切面切面是通知和切點的結合。 面向切面的Spring 本章主要內容: 面向切面編程的基本原理 通過POJO創建切面 使用@Aspect注解 為AspectJ切面注入依賴。 說明 ...
摘要:之旅簡化開發的使命簡化開發為了降低開發的復雜性,采取如下關鍵策略基于的輕量級和最小侵入性編程通過依賴注入和面向接口實現松耦合基于切面和慣例進行聲明式編程通過切面和模版減少樣式代碼依賴注入耦合性具有兩面性一方面,緊密耦合的代碼難以測試難以復 Spring之旅 簡化Java開發 Spring的使命:簡化Java開發 為了降低Java開發的復雜性,采取如下關鍵策略:基于POJO的輕量級和最...
摘要:除了,還簡單介紹了對的支持,可以幫助應用將散落在各處的邏輯匯集于一處切面。當裝配的時候,這些切面能夠運行期編織起來,這樣就能呢個非常有效的賦予新功能。 第1章 Spring之旅 說明 1、本文參考了《Spring 實戰》重點內容,參考了GitHub上的代碼 2、每個人的學習方式不一樣,但目的是一樣的,活學活用。最近一直在聽《我們不一樣》 3、本文只為記錄作為以后參考,要想真正領悟Sp...
摘要:兩種方式來表示的應用上下文。日志,事務管理和安全這樣的系統服務經常融入到具有核心業務邏輯的組件中去,這些系統服務通過被稱為橫切關注點。容器使用管理構成應用的組件,他會創建相互協作的組件之間的關聯。的生命周期四俯瞰的風景線模塊,,,, 完整代碼請見:https://github.com/codercuixi...為了降低Java開發的復雜性,Spring采用了以下4種策略: 基于poj...
摘要:前戲補上參會的完整記錄,這個問題從一開始我就是準備自問自答的,希望可以通過這種形式把大會的干貨分享給更多人。 showImg(http://7xqy7v.com1.z0.glb.clouddn.com/colorful/blog/feday2.png); 前戲 2016/3/21 補上參會的完整記錄,這個問題從一開始我就是準備自問自答的,希望可以通過這種形式把大會的干貨分享給更多人。 ...
閱讀 1823·2021-09-22 15:54
閱讀 2935·2021-09-01 10:42
閱讀 3448·2019-08-30 15:56
閱讀 1443·2019-08-29 18:46
閱讀 2473·2019-08-29 10:57
閱讀 2715·2019-08-28 17:57
閱讀 3667·2019-08-23 18:14
閱讀 839·2019-08-23 17:03